diff options
author | ume <ume@FreeBSD.org> | 2003-10-22 15:29:42 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2003-10-22 15:29:42 +0000 |
commit | 55ad7e749c00f530d95cf66a3856d65112354e22 (patch) | |
tree | 5ac597532f08a9661e11df5fea8e41b0138b55ea /sys | |
parent | 75c947372b01f8404a3adaae4c3eec28c119fd0f (diff) | |
download | FreeBSD-src-55ad7e749c00f530d95cf66a3856d65112354e22.zip FreeBSD-src-55ad7e749c00f530d95cf66a3856d65112354e22.tar.gz |
- implement lock around IPv6 reassembly, to avoid panic due to
frag6_drain (mutex version will come later).
- limit number of fragments (not fragment queues) in kernel.
Obtained from: KAME
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netinet6/frag6.c | 106 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 4 | ||||
-rw-r--r-- | sys/netinet6/in6_proto.c | 3 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 2 |
4 files changed, 99 insertions, 16 deletions
diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c index 5fae151..fe2b8e5 100644 --- a/sys/netinet6/frag6.c +++ b/sys/netinet6/frag6.c @@ -68,12 +68,51 @@ static void frag6_insque __P((struct ip6q *, struct ip6q *)); static void frag6_remque __P((struct ip6q *)); static void frag6_freef __P((struct ip6q *)); -/* XXX we eventually need splreass6, or some real semaphore */ -int frag6_doing_reass; +static int ip6q_locked; u_int frag6_nfragpackets; +u_int frag6_nfrags; struct ip6q ip6q; /* ip6 reassemble queue */ -/* FreeBSD tweak */ +static __inline int ip6q_lock_try __P((void)); +static __inline void ip6q_unlock __P((void)); + +static __inline int +ip6q_lock_try() +{ + if (ip6q_locked) + return (0); + ip6q_locked = 1; + return (1); +} + +static __inline void +ip6q_unlock() +{ + ip6q_locked = 0; +} + +#ifdef DIAGNOSTIC +#define IP6Q_LOCK() \ +do { \ + if (ip6q_lock_try() == 0) { \ + printf("%s:%d: ip6q already locked\n", __FILE__, __LINE__); \ + panic("ip6q_lock"); \ + } \ +} while (/*CONSTCOND*/ 0) +#define IP6Q_LOCK_CHECK() \ +do { \ + if (ip6q_locked == 0) { \ + printf("%s:%d: ip6q lock not held\n", __FILE__, __LINE__); \ + panic("ip6q lock check"); \ + } \ +} while (/*CONSTCOND*/ 0) +#else +#define IP6Q_LOCK() (void) ip6q_lock_try() +#define IP6Q_LOCK_CHECK() /* nothing */ +#endif + +#define IP6Q_UNLOCK() ip6q_unlock() + static MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header"); /* @@ -84,6 +123,7 @@ frag6_init() { ip6_maxfragpackets = nmbclusters / 4; + ip6_maxfrags = nmbclusters / 4; #ifndef RANDOM_IP_ID ip6_id = arc4random(); @@ -204,7 +244,17 @@ frag6_input(mp, offp, proto) /* offset now points to data portion */ offset += sizeof(struct ip6_frag); - frag6_doing_reass = 1; + IP6Q_LOCK(); + + /* + * Enforce upper bound on number of fragments. + * If maxfrag is 0, never accept fragments. + * If maxfrag is -1, accept all fragments without limitation. + */ + if (ip6_maxfrags < 0) + ; + else if (frag6_nfrags >= (u_int)ip6_maxfrags) + goto dropfrag; for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && @@ -221,8 +271,9 @@ frag6_input(mp, offp, proto) /* * Enforce upper bound on number of fragmented packets * for which we attempt reassembly; - * If maxfrag is 0, never accept fragments. - * If maxfrag is -1, accept all fragments without limitation. + * If maxfragpackets is 0, never accept fragments. + * If maxfragpackets is -1, accept all fragments without + * limitation. */ if (ip6_maxfragpackets < 0) ; @@ -248,6 +299,8 @@ frag6_input(mp, offp, proto) q6->ip6q_src = ip6->ip6_src; q6->ip6q_dst = ip6->ip6_dst; q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ + + q6->ip6q_nfrag = 0; } /* @@ -273,14 +326,14 @@ frag6_input(mp, offp, proto) icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset - sizeof(struct ip6_frag) + offsetof(struct ip6_frag, ip6f_offlg)); - frag6_doing_reass = 0; + IP6Q_UNLOCK(); return (IPPROTO_DONE); } } else if (fragoff + frgpartlen > IPV6_MAXPACKET) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset - sizeof(struct ip6_frag) + offsetof(struct ip6_frag, ip6f_offlg)); - frag6_doing_reass = 0; + IP6Q_UNLOCK(); return (IPPROTO_DONE); } /* @@ -388,6 +441,8 @@ frag6_input(mp, offp, proto) * If the incoming framgent overlaps some existing fragments in * the reassembly queue, drop it, since it is dangerous to override * existing fragments from a security point of view. + * We don't know which fragment is the bad guy - here we trust + * fragment that came in earlier, with no real reason. */ if (af6->ip6af_up != (struct ip6asfrag *)q6) { i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen @@ -425,6 +480,8 @@ insert: * the most recently active fragmented packet. */ frag6_enq(ip6af, af6->ip6af_up); + frag6_nfrags++; + q6->ip6q_nfrag++; #if 0 /* xxx */ if (q6 != ip6q.ip6q_next) { frag6_remque(q6); @@ -435,13 +492,13 @@ insert: for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = af6->ip6af_down) { if (af6->ip6af_off != next) { - frag6_doing_reass = 0; + IP6Q_UNLOCK(); return IPPROTO_DONE; } next += af6->ip6af_frglen; } if (af6->ip6af_up->ip6af_mff) { - frag6_doing_reass = 0; + IP6Q_UNLOCK(); return IPPROTO_DONE; } @@ -487,6 +544,7 @@ insert: /* this comes with no copy if the boundary is on cluster */ if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) { frag6_remque(q6); + frag6_nfrags -= q6->ip6q_nfrag; free(q6, M_FTABLE); frag6_nfragpackets--; goto dropfrag; @@ -504,6 +562,7 @@ insert: } frag6_remque(q6); + frag6_nfrags -= q6->ip6q_nfrag; free(q6, M_FTABLE); frag6_nfragpackets--; @@ -524,14 +583,14 @@ insert: *mp = m; *offp = offset; - frag6_doing_reass = 0; + IP6Q_UNLOCK(); return nxt; dropfrag: in6_ifstat_inc(dstifp, ifs6_reass_fail); ip6stat.ip6s_fragdropped++; m_freem(m); - frag6_doing_reass = 0; + IP6Q_UNLOCK(); return IPPROTO_DONE; } @@ -545,6 +604,8 @@ frag6_freef(q6) { struct ip6asfrag *af6, *down6; + IP6Q_LOCK_CHECK(); + for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = down6) { struct mbuf *m = IP6_REASS_MBUF(af6); @@ -573,6 +634,7 @@ frag6_freef(q6) free(af6, M_FTABLE); } frag6_remque(q6); + frag6_nfrags -= q6->ip6q_nfrag; free(q6, M_FTABLE); frag6_nfragpackets--; } @@ -585,6 +647,9 @@ void frag6_enq(af6, up6) struct ip6asfrag *af6, *up6; { + + IP6Q_LOCK_CHECK(); + af6->ip6af_up = up6; af6->ip6af_down = up6->ip6af_down; up6->ip6af_down->ip6af_up = af6; @@ -598,6 +663,9 @@ void frag6_deq(af6) struct ip6asfrag *af6; { + + IP6Q_LOCK_CHECK(); + af6->ip6af_up->ip6af_down = af6->ip6af_down; af6->ip6af_down->ip6af_up = af6->ip6af_up; } @@ -606,6 +674,9 @@ void frag6_insque(new, old) struct ip6q *new, *old; { + + IP6Q_LOCK_CHECK(); + new->ip6q_prev = old; new->ip6q_next = old->ip6q_next; old->ip6q_next->ip6q_prev= new; @@ -616,6 +687,9 @@ void frag6_remque(p6) struct ip6q *p6; { + + IP6Q_LOCK_CHECK(); + p6->ip6q_prev->ip6q_next = p6->ip6q_next; p6->ip6q_next->ip6q_prev = p6->ip6q_prev; } @@ -631,7 +705,7 @@ frag6_slowtimo() struct ip6q *q6; int s = splnet(); - frag6_doing_reass = 1; + IP6Q_LOCK(); q6 = ip6q.ip6q_next; if (q6) while (q6 != &ip6q) { @@ -654,7 +728,7 @@ frag6_slowtimo() /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(ip6q.ip6q_prev); } - frag6_doing_reass = 0; + IP6Q_UNLOCK(); #if 0 /* @@ -681,11 +755,13 @@ frag6_slowtimo() void frag6_drain() { - if (frag6_doing_reass) + + if (ip6q_lock_try() == 0) return; while (ip6q.ip6q_next != &ip6q) { ip6stat.ip6s_fragdropped++; /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(ip6q.ip6q_next); } + IP6Q_UNLOCK(); } diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index e31e45a..92127f3 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -539,9 +539,11 @@ struct in6_pktinfo { #define IPV6CTL_AUTO_LINKLOCAL 35 /* automatic link-local addr assign */ #define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */ +#define IPV6CTL_MAXFRAGS 41 /* max fragments */ + /* New entries should be added here from current IPV6CTL_MAXID value. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ -#define IPV6CTL_MAXID 37 +#define IPV6CTL_MAXID 42 /* * Redefinition of mbuf flags diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c index 7f477a6..d8c308f 100644 --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -290,6 +290,7 @@ int ip6_defhlim = IPV6_DEFHLIM; int ip6_defmcasthlim = IPV6_DEFAULT_MULTICAST_HOPS; int ip6_accept_rtadv = 0; /* "IPV6FORWARDING ? 0 : 1" is dangerous */ int ip6_maxfragpackets; /* initialized in frag6.c:frag6_init() */ +int ip6_maxfrags; /* initialized in frag6.c:frag6_init() */ int ip6_log_interval = 5; int ip6_hdrnestlimit = 50; /* appropriate? */ int ip6_dad_count = 1; /* DupAddrDetectionTransmits */ @@ -438,6 +439,8 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_LINKLOCAL, auto_linklocal, CTLFLAG_RW, &ip6_auto_linklocal, 0, ""); SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_RIP6STATS, rip6stats, CTLFLAG_RD, &rip6stat, rip6stat, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGS, + maxfrags, CTLFLAG_RW, &ip6_maxfrags, 0, ""); /* net.inet6.icmp6 */ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRACCEPT, diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 006becd..dc96ed7 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -89,6 +89,7 @@ struct ip6q { #ifdef notyet u_char *ip6q_nxtp; #endif + int ip6q_nfrag; /* # of fragments */ }; struct ip6asfrag { @@ -267,6 +268,7 @@ extern int ip6_v6only; extern struct socket *ip6_mrouter; /* multicast routing daemon */ extern int ip6_sendredirects; /* send IP redirects when forwarding? */ extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */ +extern int ip6_maxfrags; /* Maximum fragments in reassembly queue */ extern int ip6_sourcecheck; /* Verify source interface */ extern int ip6_sourcecheck_interval; /* Interval between log messages */ extern int ip6_accept_rtadv; /* Acts as a host not a router */ |