From 1a5d6f5e0bde40b44ad964622e145eea24c5ca30 Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Thu, 27 Dec 2001 16:49:31 +0000 Subject: [PATCH] Implement VJ header compression for sppp. This is the logical merge of rev 1.32 of i4b's old if_spppsubr.c (which was based on PR misc/11767), plus (i4b) rev 1.6 of i4b's if_ispppsubr.c, albeit with numerous stylistic and cosmetic changes. PR: misc/11767 Submitted by: i4b, Joachim Kuebart MFC after: 1 month --- sys/net/if_sppp.h | 5 ++ sys/net/if_spppsubr.c | 158 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 158 insertions(+), 5 deletions(-) diff --git a/sys/net/if_sppp.h b/sys/net/if_sppp.h index 7560f5950566..c2d3e7d47509 100644 --- a/sys/net/if_sppp.h +++ b/sys/net/if_sppp.h @@ -51,6 +51,9 @@ struct sipcp { #define IPV6CP_MYIFID_DYN 8 /* my ifid is dynamically assigned */ #endif #define IPV6CP_MYIFID_SEEN 0x10 /* have seen his ifid already */ +#define IPCP_VJ 0x20 /* can use VJ compression */ + int max_state; /* VJ: Max-Slot-Id */ + int compress_cid; /* VJ: Comp-Slot-Id */ }; #define AUTHNAMELEN 32 @@ -99,6 +102,7 @@ struct sppp { u_char confid[IDX_COUNT]; /* id of last configuration request */ int rst_counter[IDX_COUNT]; /* restart counter */ int fail_counter[IDX_COUNT]; /* negotiation failure counter */ + int enable_vj; /* VJ header compression enabled */ struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */ struct callout_handle pap_my_to_ch; /* PAP needs one more... */ struct slcp lcp; /* LCP params */ @@ -106,6 +110,7 @@ struct sppp { struct sipcp ipv6cp; /* IPv6CP params */ struct sauth myauth; /* auth params, i'm peer */ struct sauth hisauth; /* auth params, i'm authenticator */ + struct slcompress pp_comp; /* for VJ compression */ /* * These functions are filled in by sppp_attach(), and are * expected to be used by the lower layer (hardware) drivers diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index 2d3d97ff85bc..44db500c27d0 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -6,7 +6,7 @@ * Author: Serge Vakulenko, * * Heavily revamped to conform to RFC 1661. - * Copyright (C) 1997, Joerg Wunsch. + * Copyright (C) 1997, 2001 Joerg Wunsch. * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -58,6 +58,10 @@ #include #include #include +#include +#include +#include +#include #if defined (__NetBSD__) || defined (__OpenBSD__) #include /* XXX for softnet */ @@ -135,6 +139,8 @@ #define PPP_ISO 0x0023 /* ISO OSI Protocol */ #define PPP_XNS 0x0025 /* Xerox NS Protocol */ #define PPP_IPX 0x002b /* Novell IPX Protocol */ +#define PPP_VJ_COMP 0x002d /* VJ compressed TCP/IP */ +#define PPP_VJ_UCOMP 0x002f /* VJ uncompressed TCP/IP */ #define PPP_IPV6 0x0057 /* Internet Protocol Version 6 */ #define PPP_LCP 0xc021 /* Link Control Protocol */ #define PPP_PAP 0xc023 /* Password Authentication Protocol */ @@ -170,6 +176,8 @@ #define IPV6CP_OPT_IFID 1 /* interface identifier */ #define IPV6CP_OPT_COMPRESSION 2 /* IPv6 compression protocol */ +#define IPCP_COMP_VJ 0x2d /* Code for VJ compression */ + #define PAP_REQ 1 /* PAP name/password request */ #define PAP_ACK 2 /* PAP acknowledge */ #define PAP_NAK 3 /* PAP fail */ @@ -500,6 +508,7 @@ sppp_input(struct ifnet *ifp, struct mbuf *m) struct ppp_header *h; struct ifqueue *inq = 0; struct sppp *sp = (struct sppp *)ifp; + int len; int debug = ifp->if_flags & IFF_DEBUG; if (ifp->if_flags & IFF_UP) @@ -590,6 +599,32 @@ sppp_input(struct ifnet *ifp, struct mbuf *m) inq = &ip6intrq; } break; + case PPP_VJ_COMP: + if (sp->state[IDX_IPCP] == STATE_OPENED) { + if ((len = + sl_uncompress_tcp((u_char **)&m->m_data, + m->m_len, + TYPE_COMPRESSED_TCP, + &sp->pp_comp)) <= 0) + goto drop; + m->m_len = m->m_pkthdr.len = len; + schednetisr (NETISR_IP); + inq = &ipintrq; + } + break; + case PPP_VJ_UCOMP: + if (sp->state[IDX_IPCP] == STATE_OPENED) { + if ((len = + sl_uncompress_tcp((u_char **)&m->m_data, + m->m_len, + TYPE_UNCOMPRESSED_TCP, + &sp->pp_comp)) <= 0) + goto drop; + m->m_len = m->m_pkthdr.len = len; + schednetisr (NETISR_IP); + inq = &ipintrq; + } + break; #endif #ifdef IPX case PPP_IPX: @@ -691,6 +726,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, struct ppp_header *h; struct ifqueue *ifq = NULL; int s, rv = 0; + int ipproto = PPP_IP; int debug = ifp->if_flags & IFF_DEBUG; s = splimp(); @@ -757,6 +793,28 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, ifq = &sp->pp_fastq; else if (INTERACTIVE (ntohs (tcp->th_dport))) ifq = &sp->pp_fastq; + + /* + * Do IP Header compression + */ + if (sp->pp_mode != IFF_CISCO && (sp->ipcp.flags & IPCP_VJ) && + ip->ip_p == IPPROTO_TCP) + switch (sl_compress_tcp(m, ip, &sp->pp_comp, + sp->ipcp.compress_cid)) { + case TYPE_COMPRESSED_TCP: + ipproto = PPP_VJ_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + ipproto = PPP_VJ_UCOMP; + break; + case TYPE_IP: + ipproto = PPP_IP; + break; + default: + m_freem(m); + splx(s); + return (EINVAL); + } } #endif @@ -806,7 +864,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, * not ready to carry IP packets, and return * ENETDOWN, as opposed to ENOBUFS. */ - h->protocol = htons(PPP_IP); + h->protocol = htons(ipproto); if (sp->state[IDX_IPCP] != STATE_OPENED) rv = ENETDOWN; } @@ -896,7 +954,8 @@ sppp_attach(struct ifnet *ifp) sp->pp_down = lcp.Down; mtx_init(&sp->pp_cpq.ifq_mtx, "sppp_cpq", MTX_DEF); mtx_init(&sp->pp_fastq.ifq_mtx, "sppp_fastq", MTX_DEF); - + sp->enable_vj = 1; + sl_compress_init(&sp->pp_comp, -1); sppp_lcp_init(sp); sppp_ipcp_init(sp); sppp_ipv6cp_init(sp); @@ -2689,7 +2748,8 @@ sppp_ipcp_open(struct sppp *sp) STDDCL; u_long myaddr, hisaddr; - sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN); + sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN | IPCP_MYADDR_SEEN | + IPCP_MYADDR_DYN | IPCP_VJ); sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0); /* @@ -2705,7 +2765,6 @@ sppp_ipcp_open(struct sppp *sp) SPP_ARGS(ifp)); return; } - if (myaddr == 0L) { /* * I don't have an assigned address, so i need to @@ -2715,6 +2774,11 @@ sppp_ipcp_open(struct sppp *sp) sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); } else sp->ipcp.flags |= IPCP_MYADDR_SEEN; + if (sp->enable_vj) { + sp->ipcp.opts |= (1 << IPCP_OPT_COMPRESSION); + sp->ipcp.max_state = MAX_STATES - 1; + sp->ipcp.compress_cid = 1; + } sppp_open_event(&ipcp, sp); } @@ -2749,6 +2813,7 @@ sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len) int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG; u_long hisaddr, desiredaddr; int gotmyaddr = 0; + int desiredcomp; len -= 4; origlen = len; @@ -2769,6 +2834,37 @@ sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len) if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { + case IPCP_OPT_COMPRESSION: + if (!sp->enable_vj) { + /* VJ compression administratively disabled */ + if (debug) + log(-1, "[locally disabled] "); + break; + } + /* + * In theory, we should only conf-rej an + * option that is shorter than RFC 1618 + * requires (i.e. < 4), and should conf-nak + * anything else that is not VJ. However, + * since our algorithm always uses the + * original option to NAK it with new values, + * things would become more complicated. In + * pratice, the only commonly implemented IP + * compression option is VJ anyway, so the + * difference is negligible. + */ + if (len >= 6 && p[1] == 6) { + /* + * correctly formed compression option + * that could be VJ compression + */ + continue; + } + if (debug) + log(-1, + "optlen %d [invalid/unsupported] ", + p[1]); + break; case IPCP_OPT_ADDRESS: if (len >= 6 && p[1] == 6) { /* correctly formed address option */ @@ -2807,6 +2903,27 @@ sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len) if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { + case IPCP_OPT_COMPRESSION: + desiredcomp = p[2] << 8 | p[3]; + /* We only support VJ */ + if (desiredcomp == IPCP_COMP_VJ) { + if (debug) + log(-1, "VJ [ack] "); + sp->ipcp.flags |= IPCP_VJ; + sl_compress_init(&sp->pp_comp, p[4]); + sp->ipcp.max_state = p[4]; + sp->ipcp.compress_cid = p[5]; + continue; + } + if (debug) + log(-1, + "compproto %#04x [not supported] ", + desiredcomp); + p[2] = IPCP_COMP_VJ >> 8; + p[3] = IPCP_COMP_VJ; + p[4] = sp->ipcp.max_state; + p[5] = sp->ipcp.compress_cid; + break; case IPCP_OPT_ADDRESS: /* This is the address he wants in his end */ desiredaddr = p[2] << 24 | p[3] << 16 | @@ -2917,6 +3034,9 @@ sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { + case IPCP_OPT_COMPRESSION: + sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESSION); + break; case IPCP_OPT_ADDRESS: /* * Peer doesn't grok address option. This is @@ -2943,6 +3063,7 @@ sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) u_char *buf, *p; struct ifnet *ifp = &sp->pp_if; int debug = ifp->if_flags & IFF_DEBUG; + int desiredcomp; u_long wantaddr; len -= 4; @@ -2959,6 +3080,23 @@ sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { + case IPCP_OPT_COMPRESSION: + if (len >= 6 && p[1] == 6) { + desiredcomp = p[2] << 8 | p[3]; + if (debug) + log(-1, "[wantcomp %#04x] ", + desiredcomp); + if (desiredcomp == IPCP_COMP_VJ) { + sl_compress_init(&sp->pp_comp, p[4]); + sp->ipcp.max_state = p[4]; + sp->ipcp.compress_cid = p[5]; + if (debug) + log(-1, "[agree] "); + } else + sp->ipcp.opts &= + ~(1 << IPCP_OPT_COMPRESSION); + } + break; case IPCP_OPT_ADDRESS: /* * Peer doesn't like our local IP address. See @@ -3031,6 +3169,14 @@ sppp_ipcp_scr(struct sppp *sp) u_long ouraddr; int i = 0; + if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) { + opt[i++] = IPCP_OPT_COMPRESSION; + opt[i++] = 6; + opt[i++] = IPCP_COMP_VJ >> 8; + opt[i++] = IPCP_COMP_VJ; + opt[i++] = sp->ipcp.max_state; + opt[i++] = sp->ipcp.compress_cid; + } if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) { sppp_get_ip_addrs(sp, &ouraddr, 0, 0); opt[i++] = IPCP_OPT_ADDRESS; @@ -4858,6 +5004,8 @@ sppp_params(struct sppp *sp, u_long cmd, void *data) bcopy(spr.defs.hisauth.secret, sp->hisauth.secret, AUTHKEYLEN); } + /* set VJ enable flag */ + sp->enable_vj = spr.defs.enable_vj; break; default: