Module Name: src Committed By: rmind Date: Mon Jul 19 14:09:45 UTC 2010
Modified Files: src/sys/netinet: in_var.h ip_input.c ip_reass.c ip_var.h Log Message: Abstract IP reassembly into single generic routine - ip_reass_packet(). Make struct ipq private and struct ipqent not visible to userland. Push ip_len adjustment into reassembly layer. OK matt@ To generate a diff of this commit: cvs rdiff -u -r1.63 -r1.64 src/sys/netinet/in_var.h cvs rdiff -u -r1.288 -r1.289 src/sys/netinet/ip_input.c cvs rdiff -u -r1.1 -r1.2 src/sys/netinet/ip_reass.c cvs rdiff -u -r1.92 -r1.93 src/sys/netinet/ip_var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netinet/in_var.h diff -u src/sys/netinet/in_var.h:1.63 src/sys/netinet/in_var.h:1.64 --- src/sys/netinet/in_var.h:1.63 Tue Jul 13 22:16:10 2010 +++ src/sys/netinet/in_var.h Mon Jul 19 14:09:44 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: in_var.h,v 1.63 2010/07/13 22:16:10 rmind Exp $ */ +/* $NetBSD: in_var.h,v 1.64 2010/07/19 14:09:44 rmind Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -300,7 +300,6 @@ struct lwp *); void in_purgeaddr(struct ifaddr *); void in_purgeif(struct ifnet *); -void ip_reass_init(void); void ip_input(struct mbuf *); int ipflow_fastforward(struct mbuf *); void ip_initid(void); Index: src/sys/netinet/ip_input.c diff -u src/sys/netinet/ip_input.c:1.288 src/sys/netinet/ip_input.c:1.289 --- src/sys/netinet/ip_input.c:1.288 Tue Jul 13 22:16:10 2010 +++ src/sys/netinet/ip_input.c Mon Jul 19 14:09:45 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_input.c,v 1.288 2010/07/13 22:16:10 rmind Exp $ */ +/* $NetBSD: ip_input.c,v 1.289 2010/07/19 14:09:45 rmind Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -91,7 +91,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.288 2010/07/13 22:16:10 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.289 2010/07/19 14:09:45 rmind Exp $"); #include "opt_inet.h" #include "opt_compat_netbsd.h" @@ -412,14 +412,11 @@ struct m_tag *mtag; struct tdb_ident *tdbi; struct secpolicy *sp; - int error; + int error, s; #endif /* FAST_IPSEC */ MCLAIM(m, &ip_rx_mowner); -#ifdef DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) - panic("ipintr no HDR"); -#endif + KASSERT((m->m_flags & M_PKTHDR) != 0); /* * If no IP addresses have been set yet but the interfaces @@ -809,8 +806,8 @@ * If offset or IP_MF are set, must reassemble. */ if (ip->ip_off & ~htons(IP_DF|IP_RF)) { - struct ipq *fp; - u_int off, hash; + struct mbuf *m_final; + u_int off, flen; bool mff; /* @@ -825,65 +822,44 @@ goto bad; } - /* - * Adjust total IP length to not reflect header. Set 'mff' - * indicator, if more fragments are expected. Convert offset - * of this to bytes. - */ - ip->ip_len = htons(ntohs(ip->ip_len) - hlen); + /* Fragment length and MF flag. */ + flen = ntohs(ip->ip_len) - hlen; mff = (ip->ip_off & htons(IP_MF)) != 0; if (mff) { /* * Make sure that fragments have a data length * which is non-zero and multiple of 8 bytes. */ - if (ntohs(ip->ip_len) == 0 || - (ntohs(ip->ip_len) & 0x7) != 0) { + if (flen == 0 || (flen & 0x7) != 0) { IP_STATINC(IP_STAT_BADFRAGS); goto bad; } } - ip->ip_off = htons((ntohs(ip->ip_off) & IP_OFFMASK) << 3); - - /* Look for queue of fragments of this datagram. */ - fp = ip_reass_lookup(ip, &hash); - - /* Make sure the TOS matches previous fragments. */ - if (fp && fp->ipq_tos != ip->ip_tos) { - IP_STATINC(IP_STAT_BADFRAGS); - ip_reass_unlock(); - goto bad; - } /* - * If datagram marked as having more fragments - * or if this is not the first fragment, - * attempt reassembly; if it succeeds, proceed. + * Adjust total IP length to not reflect header and convert + * offset of this to bytes. XXX: clobbers struct ip. */ - if (mff || ip->ip_off != htons(0)) { - struct ipqent *ipqe; + ip->ip_len = htons(flen); + ip->ip_off = htons(off); - ipqe = ip_reass_getent(); - if (ipqe == NULL) { - IP_STATINC(IP_STAT_RCVMEMDROP); - ip_reass_unlock(); - goto bad; - } - ipqe->ipqe_mff = mff; - ipqe->ipqe_m = m; - ipqe->ipqe_ip = ip; - m = ip_reass(ipqe, fp, hash); - if (m == NULL) { - return; - } - IP_STATINC(IP_STAT_REASSEMBLED); - ip = mtod(m, struct ip *); - hlen = ip->ip_hl << 2; - ip->ip_len = htons(ntohs(ip->ip_len) + hlen); - } else if (fp) { - ip_freef(fp); - ip_reass_unlock(); + /* + * Pass to IP reassembly mechanism. + */ + if (ip_reass_packet(m, ip, mff, &m_final) != 0) { + /* Failed; invalid fragment(s) or packet. */ + goto bad; } + if (m_final == NULL) { + /* More fragments should come; silently return. */ + return; + } + /* Reassembly is done, we have the final packet. */ + m = m_final; + + /* Updated local variable(s). */ + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; } #if defined(IPSEC) Index: src/sys/netinet/ip_reass.c diff -u src/sys/netinet/ip_reass.c:1.1 src/sys/netinet/ip_reass.c:1.2 --- src/sys/netinet/ip_reass.c:1.1 Tue Jul 13 22:16:10 2010 +++ src/sys/netinet/ip_reass.c Mon Jul 19 14:09:45 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_reass.c,v 1.1 2010/07/13 22:16:10 rmind Exp $ */ +/* $NetBSD: ip_reass.c,v 1.2 2010/07/19 14:09:45 rmind Exp $ */ /* * Copyright (c) 1982, 1986, 1988, 1993 @@ -46,17 +46,19 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ip_reass.c,v 1.1 2010/07/13 22:16:10 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_reass.c,v 1.2 2010/07/19 14:09:45 rmind Exp $"); #include <sys/param.h> -#include <sys/systm.h> +#include <sys/types.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> #include <sys/pool.h> +#include <sys/queue.h> #include <sys/sysctl.h> +#include <sys/systm.h> #include <net/if.h> #include <net/route.h> @@ -65,10 +67,10 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/in_pcb.h> +#include <netinet/ip_var.h> #include <netinet/in_proto.h> #include <netinet/ip_private.h> #include <netinet/in_var.h> -#include <netinet/ip_var.h> /* * IP datagram reassembly hashed queues, pool, lock and counters. @@ -90,6 +92,23 @@ static int ip_maxfrags; /* limit on fragments. XXX sysctl */ /* + * IP reassembly queue structure. Each fragment being reassembled is + * attached to one of these structures. They are timed out after ipq_ttl + * drops to 0, and may also be reclaimed if memory becomes tight. + */ +struct ipq { + LIST_ENTRY(ipq) ipq_q; /* to other reass headers */ + uint8_t ipq_ttl; /* time for reass q to live */ + uint8_t ipq_p; /* protocol of this fragment */ + uint16_t ipq_id; /* sequence id for reassembly */ + struct ipqehead ipq_fragq; /* to ip fragment queue */ + struct in_addr ipq_src; + struct in_addr ipq_dst; + uint16_t ipq_nfrags; /* frags in this queue entry */ + uint8_t ipq_tos; /* TOS of this fragment */ +}; + +/* * Cached copy of nmbclusters. If nbclusters is different, * recalculate IP parameters derived from nmbclusters. */ @@ -102,8 +121,12 @@ void sysctl_ip_reass_setup(void); static void ip_nmbclusters_changed(void); -static u_int ip_reass_ttl_decr(u_int ticks); -static void ip_reass_drophalf(void); + +static struct ipq * ip_reass_lookup(struct ip *, u_int *); +static struct mbuf * ip_reass(struct ipqent *, struct ipq *, u_int); +static u_int ip_reass_ttl_decr(u_int ticks); +static void ip_reass_drophalf(void); +static void ip_freef(struct ipq *); /* * ip_reass_init: @@ -236,7 +259,7 @@ * * Look for queue of fragments of this datagram. */ -struct ipq * +static struct ipq * ip_reass_lookup(struct ip *ip, u_int *hashp) { struct ipq *fp; @@ -259,27 +282,6 @@ return fp; } -void -ip_reass_unlock(void) -{ - - IPQ_UNLOCK(); -} - -struct ipqent * -ip_reass_getent(void) -{ - struct ipqent *ipqe; - int s; - - IP_STATINC(IP_STAT_FRAGMENTS); - s = splvm(); - ipqe = pool_get(&ipqent_pool, PR_NOWAIT); - splx(s); - - return ipqe; -} - /* * ip_reass: * @@ -467,9 +469,10 @@ * packet. Dequeue and discard fragment reassembly header. Make * header visible. */ - ip->ip_len = htons(next); + ip->ip_len = htons((ip->ip_hl << 2) + next); ip->ip_src = fp->ipq_src; ip->ip_dst = fp->ipq_dst; + LIST_REMOVE(fp, ipq_q); free(fp, M_FTABLE); ip_nfragpackets--; @@ -506,7 +509,7 @@ * * Free a fragment reassembly header and all associated datagrams. */ -void +static void ip_freef(struct ipq *fp) { struct ipqent *q, *p; @@ -675,3 +678,53 @@ } IPQ_UNLOCK(); } + +/* + * ip_reass_packet: generic routine to perform IP reassembly. + * + * => Passed fragment should have IP_MF flag and/or offset set. + * => Fragment should not have other than IP_MF flags set. + * + * => Returns 0 on success or error otherwise. When reassembly is complete, + * m_final representing a constructed final packet is set. + */ +int +ip_reass_packet(struct mbuf *m, struct ip *ip, bool mff, struct mbuf **m_final) +{ + struct ipq *fp; + struct ipqent *ipqe; + u_int hash; + + /* Look for queue of fragments of this datagram. */ + fp = ip_reass_lookup(ip, &hash); + + /* Make sure that TOS matches previous fragments. */ + if (fp && fp->ipq_tos != ip->ip_tos) { + IP_STATINC(IP_STAT_BADFRAGS); + IPQ_UNLOCK(); + return EINVAL; + } + + /* + * Create new entry and attempt to reassembly. + */ + IP_STATINC(IP_STAT_FRAGMENTS); + int s = splvm(); + ipqe = pool_get(&ipqent_pool, PR_NOWAIT); + splx(s); + if (ipqe == NULL) { + IP_STATINC(IP_STAT_RCVMEMDROP); + IPQ_UNLOCK(); + return ENOMEM; + } + ipqe->ipqe_mff = mff; + ipqe->ipqe_m = m; + ipqe->ipqe_ip = ip; + + *m_final = ip_reass(ipqe, fp, hash); + if (*m_final) { + /* Note if finally reassembled. */ + IP_STATINC(IP_STAT_REASSEMBLED); + } + return 0; +} Index: src/sys/netinet/ip_var.h diff -u src/sys/netinet/ip_var.h:1.92 src/sys/netinet/ip_var.h:1.93 --- src/sys/netinet/ip_var.h:1.92 Tue Jul 13 22:16:10 2010 +++ src/sys/netinet/ip_var.h Mon Jul 19 14:09:45 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_var.h,v 1.92 2010/07/13 22:16:10 rmind Exp $ */ +/* $NetBSD: ip_var.h,v 1.93 2010/07/19 14:09:45 rmind Exp $ */ /* * Copyright (c) 1982, 1986, 1993 @@ -48,6 +48,8 @@ struct in_addr ih_dst; /* destination internet address */ } __packed; +#ifdef _KERNEL + /* * Ip (reassembly or sequence) queue structures. * @@ -80,22 +82,7 @@ #define ipqe_ip _ipqe_u1._ip #define ipqe_tcp _ipqe_u1._tcp -/* - * Ip reassembly queue structure. Each fragment - * being reassembled is attached to one of these structures. - * They are timed out after ipq_ttl drops to 0, and may also - * be reclaimed if memory becomes tight. - */ -struct ipq { - LIST_ENTRY(ipq) ipq_q; /* to other reass headers */ - u_int8_t ipq_ttl; /* time for reass q to live */ - u_int8_t ipq_p; /* protocol of this fragment */ - u_int16_t ipq_id; /* sequence id for reassembly */ - struct ipqehead ipq_fragq; /* to ip fragment queue */ - struct in_addr ipq_src, ipq_dst; - u_int16_t ipq_nfrags; /* frags in this queue entry */ - u_int8_t ipq_tos; /* TOS of this fragment */ -}; +#endif /* _KERNEL */ /* * Structure stored in mbuf in inpcb.ip_options @@ -214,16 +201,10 @@ int ip_fragment(struct mbuf *, struct ifnet *, u_long); int ip_pcbopts(struct mbuf **, const struct sockopt *); -struct ipq * - ip_reass_lookup(struct ip *, u_int *); -void ip_reass_unlock(void); -struct ipqent * - ip_reass_getent(void); -struct mbuf * - ip_reass(struct ipqent *, struct ipq *, u_int); +void ip_reass_init(void); +int ip_reass_packet(struct mbuf *, struct ip *, bool, struct mbuf **); void ip_reass_slowtimo(void); void ip_reass_drain(void); -void ip_freef(struct ipq *); struct in_ifaddr * ip_rtaddr(struct in_addr);