Date: Wed, 23 Oct 2019 23:01:18 +0000 (UTC) From: "Bjoern A. Zeeb" <bz@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r353965 - head/sys/netinet6 Message-ID: <201910232301.x9NN1ITc062893@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: bz Date: Wed Oct 23 23:01:18 2019 New Revision: 353965 URL: https://svnweb.freebsd.org/changeset/base/353965 Log: frag6: replace KAME hand-rolled queues with queue(9) TAILQs Remove the KAME custom circular queue for fragments and fragmented packets and replace them with a standard TAILQ. This make the code a lot more understandable and maintainable and removes further hand-rolled code from the the tree using a standard interface instead. Hide the still public structures under #ifdef _KERNEL as there is no use for them in user space. The naming is a bit confusing now as struct ip6q and the ip6q[] buckets array are not the same anymore; sadly struct ip6q is also used by the MAC framework and we cannot rename it. Submitted by: jtl (initally) MFC after: 3 weeks Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D16847 (jtl's original) Modified: head/sys/netinet6/frag6.c head/sys/netinet6/ip6_var.h Modified: head/sys/netinet6/frag6.c ============================================================================== --- head/sys/netinet6/frag6.c Wed Oct 23 20:39:21 2019 (r353964) +++ head/sys/netinet6/frag6.c Wed Oct 23 23:01:18 2019 (r353965) @@ -3,6 +3,7 @@ * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. + * Copyright (c) 2019 Netflix, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -45,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> +#include <sys/queue.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <sys/syslog.h> @@ -72,28 +74,20 @@ __FBSDID("$FreeBSD$"); #define IP6REASS_NHASH (1 << IP6REASS_NHASH_LOG2) #define IP6REASS_HMASK (IP6REASS_NHASH - 1) -static void frag6_enq(struct ip6asfrag *, struct ip6asfrag *, - uint32_t bucket __unused); -static void frag6_deq(struct ip6asfrag *, uint32_t bucket __unused); -static void frag6_insque_head(struct ip6q *, struct ip6q *, - uint32_t bucket); -static void frag6_remque(struct ip6q *, uint32_t bucket); -static void frag6_freef(struct ip6q *, uint32_t bucket); - +TAILQ_HEAD(ip6qhead, ip6q); struct ip6qbucket { - struct ip6q ip6q; + struct ip6qhead packets; struct mtx lock; int count; }; -struct ip6asfrag { - struct ip6asfrag *ip6af_down; - struct ip6asfrag *ip6af_up; +struct ip6asfrag { + TAILQ_ENTRY(ip6asfrag) ip6af_tq; struct mbuf *ip6af_m; int ip6af_offset; /* offset in ip6af_m to next header */ int ip6af_frglen; /* fragmentable part length */ int ip6af_off; /* fragment offset */ - u_int16_t ip6af_mff; /* more fragment bit in frag off */ + bool ip6af_mff; /* more fragment bit in frag off */ }; #define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m)) @@ -132,7 +126,7 @@ VNET_DEFINE_STATIC(uint32_t, ip6qb_hashseed); #define IP6QB_TRYLOCK(_b) mtx_trylock(&V_ip6qb[(_b)].lock) #define IP6QB_LOCK_ASSERT(_b) mtx_assert(&V_ip6qb[(_b)].lock, MA_OWNED) #define IP6QB_UNLOCK(_b) mtx_unlock(&V_ip6qb[(_b)].lock) -#define IP6QB_HEAD(_b) (&V_ip6qb[(_b)].ip6q) +#define IP6QB_HEAD(_b) (&V_ip6qb[(_b)].packets) /* * By default, limit the number of IP6 fragments across all reassembly @@ -240,17 +234,15 @@ static void frag6_freef(struct ip6q *q6, uint32_t bucket) { struct ip6_hdr *ip6; - struct ip6asfrag *af6, *down6; + struct ip6asfrag *af6; struct mbuf *m; IP6QB_LOCK_ASSERT(bucket); - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; - af6 = down6) { + while ((af6 = TAILQ_FIRST(&q6->ip6q_frags)) != NULL) { m = IP6_REASS_MBUF(af6); - down6 = af6->ip6af_down; - frag6_deq(af6, bucket); + TAILQ_REMOVE(&q6->ip6q_frags, af6, ip6af_tq); /* * Return ICMP time exceeded error for the 1st fragment. @@ -272,7 +264,9 @@ frag6_freef(struct ip6q *q6, uint32_t bucket) free(af6, M_FRAG6); } - frag6_remque(q6, bucket); + + TAILQ_REMOVE(IP6QB_HEAD(bucket), q6, ip6q_tq); + V_ip6qb[bucket].count--; atomic_subtract_int(&frag6_nfrags, q6->ip6q_nfrag); #ifdef MAC mac_ip6q_destroy(q6); @@ -288,10 +282,11 @@ frag6_freef(struct ip6q *q6, uint32_t bucket) static void frag6_cleanup(void *arg __unused, struct ifnet *ifp) { - struct ip6q *q6, *q6n, *head; + struct ip6qhead *head; + struct ip6q *q6; struct ip6asfrag *af6; struct mbuf *m; - int i; + uint32_t bucket; KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__)); @@ -305,15 +300,13 @@ frag6_cleanup(void *arg __unused, struct ifnet *ifp) #endif CURVNET_SET_QUIET(ifp->if_vnet); - for (i = 0; i < IP6REASS_NHASH; i++) { - IP6QB_LOCK(i); - head = IP6QB_HEAD(i); + for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) { + IP6QB_LOCK(bucket); + head = IP6QB_HEAD(bucket); /* Scan fragment list. */ - for (q6 = head->ip6q_next; q6 != head; q6 = q6n) { - q6n = q6->ip6q_next; + TAILQ_FOREACH(q6, head, ip6q_tq) { + TAILQ_FOREACH(af6, &q6->ip6q_frags, ip6af_tq) { - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; - af6 = af6->ip6af_down) { m = IP6_REASS_MBUF(af6); /* clear no longer valid rcvif pointer */ @@ -321,7 +314,7 @@ frag6_cleanup(void *arg __unused, struct ifnet *ifp) m->m_pkthdr.rcvif = NULL; } } - IP6QB_UNLOCK(i); + IP6QB_UNLOCK(bucket); } CURVNET_RESTORE(); } @@ -362,14 +355,14 @@ EVENTHANDLER_DEFINE(ifnet_departure_event, frag6_clean int frag6_input(struct mbuf **mp, int *offp, int proto) { - struct ifnet *dstifp; - struct ifnet *srcifp; - struct in6_ifaddr *ia6; + struct mbuf *m, *t; struct ip6_hdr *ip6; struct ip6_frag *ip6f; - struct ip6q *head, *q6; - struct ip6asfrag *af6, *af6dwn, *ip6af; - struct mbuf *m, *t; + struct ip6qhead *head; + struct ip6q *q6; + struct ip6asfrag *af6, *ip6af, *af6tmp; + struct in6_ifaddr *ia6; + struct ifnet *dstifp, *srcifp; uint32_t hashkey[(sizeof(struct in6_addr) * 2 + sizeof(ip6f->ip6f_ident)) / sizeof(uint32_t)]; uint32_t bucket, *hashkeyp; @@ -466,8 +459,8 @@ frag6_input(struct mbuf **mp, int *offp, int proto) *hashkeyp = ip6f->ip6f_ident; bucket = jenkins_hash32(hashkey, nitems(hashkey), V_ip6qb_hashseed); bucket &= IP6REASS_HMASK; - head = IP6QB_HEAD(bucket); IP6QB_LOCK(bucket); + head = IP6QB_HEAD(bucket); /* * Enforce upper bound on number of fragments for the entire system. @@ -479,7 +472,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto) else if (atomic_load_int(&frag6_nfrags) >= (u_int)ip6_maxfrags) goto dropfrag; - for (q6 = head->ip6q_next; q6 != head; q6 = q6->ip6q_next) + TAILQ_FOREACH(q6, head, ip6q_tq) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst) @@ -490,7 +483,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto) break; only_frag = false; - if (q6 == head) { + if (q6 == NULL) { /* A first fragment to arrive creates a reassembly queue. */ only_frag = true; @@ -522,13 +515,9 @@ frag6_input(struct mbuf **mp, int *offp, int proto) } mac_ip6q_create(m, q6); #endif - frag6_insque_head(q6, head, bucket); /* ip6q_nxt will be filled afterwards, from 1st fragment. */ - q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; -#ifdef notyet - q6->ip6q_nxtp = (u_char *)nxtp; -#endif + TAILQ_INIT(&q6->ip6q_frags); q6->ip6q_ident = ip6f->ip6f_ident; q6->ip6q_ttl = IPV6_FRAGTTL; q6->ip6q_src = ip6->ip6_src; @@ -537,7 +526,9 @@ frag6_input(struct mbuf **mp, int *offp, int proto) (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ - q6->ip6q_nfrag = 0; + /* Add the fragemented packet to the bucket. */ + TAILQ_INSERT_HEAD(head, q6, ip6q_tq); + V_ip6qb[bucket].count++; } /* @@ -577,9 +568,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto) * fragment already stored in the reassembly queue. */ if (fragoff == 0) { - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; - af6 = af6dwn) { - af6dwn = af6->ip6af_down; + TAILQ_FOREACH_SAFE(af6, &q6->ip6q_frags, ip6af_tq, af6tmp) { if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen > IPV6_MAXPACKET) { @@ -591,7 +580,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto) erroff = af6->ip6af_offset; /* Dequeue the fragment. */ - frag6_deq(af6, bucket); + TAILQ_REMOVE(&q6->ip6q_frags, af6, ip6af_tq); free(af6, M_FRAG6); /* Set a valid receive interface pointer. */ @@ -620,15 +609,19 @@ frag6_input(struct mbuf **mp, int *offp, int proto) M_NOWAIT | M_ZERO); if (ip6af == NULL) goto dropfrag; - ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; + ip6af->ip6af_mff = (ip6f->ip6f_offlg & IP6F_MORE_FRAG) ? true : false; ip6af->ip6af_off = fragoff; ip6af->ip6af_frglen = frgpartlen; ip6af->ip6af_offset = offset; IP6_REASS_MBUF(ip6af) = m; if (only_frag) { - af6 = (struct ip6asfrag *)q6; - goto insert; + /* + * Do a manual insert rather than a hard-to-understand cast + * to a different type relying on data structure order to work. + */ + TAILQ_INSERT_HEAD(&q6->ip6q_frags, ip6af, ip6af_tq); + goto postinsert; } /* Do duplicate, condition, and boundry checks. */ @@ -653,8 +646,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto) } /* Find a fragmented part which begins after this one does. */ - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; - af6 = af6->ip6af_down) + TAILQ_FOREACH(af6, &q6->ip6q_frags, ip6af_tq) if (af6->ip6af_off > ip6af->ip6af_off) break; @@ -666,14 +658,18 @@ frag6_input(struct mbuf **mp, int *offp, int proto) * drop the existing fragment and leave the fragmentation queue * unchanged, as allowed by the RFC. (RFC 8200, 4.5) */ - if (af6->ip6af_up != (struct ip6asfrag *)q6) { - if (af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen - + if (af6 != NULL) + af6tmp = TAILQ_PREV(af6, ip6fraghead, ip6af_tq); + else + af6tmp = TAILQ_LAST(&q6->ip6q_frags, ip6fraghead); + if (af6tmp != NULL) { + if (af6tmp->ip6af_off + af6tmp->ip6af_frglen - ip6af->ip6af_off > 0) { free(ip6af, M_FRAG6); goto dropfrag; } } - if (af6 != (struct ip6asfrag *)q6) { + if (af6 != NULL) { if (ip6af->ip6af_off + ip6af->ip6af_frglen - af6->ip6af_off > 0) { free(ip6af, M_FRAG6); @@ -681,10 +677,8 @@ frag6_input(struct mbuf **mp, int *offp, int proto) } } -insert: #ifdef MAC - if (!only_frag) - mac_ip6q_update(m, q6); + mac_ip6q_update(m, q6); #endif /* @@ -692,13 +686,16 @@ insert: * If not complete, check fragment limit. Move to front of packet * queue, as we are the most recently active fragmented packet. */ - frag6_enq(ip6af, af6->ip6af_up, bucket); + if (af6 != NULL) + TAILQ_INSERT_BEFORE(af6, ip6af, ip6af_tq); + else + TAILQ_INSERT_TAIL(&q6->ip6q_frags, ip6af, ip6af_tq); +postinsert: atomic_add_int(&frag6_nfrags, 1); q6->ip6q_nfrag++; plen = 0; - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; - af6 = af6->ip6af_down) { + TAILQ_FOREACH(af6, &q6->ip6q_frags, ip6af_tq) { if (af6->ip6af_off != plen) { if (q6->ip6q_nfrag > V_ip6_maxfragsperpacket) { IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag); @@ -709,7 +706,8 @@ insert: } plen += af6->ip6af_frglen; } - if (af6->ip6af_up->ip6af_mff) { + af6 = TAILQ_LAST(&q6->ip6q_frags, ip6fraghead); + if (af6->ip6af_mff) { if (q6->ip6q_nfrag > V_ip6_maxfragsperpacket) { IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag); frag6_freef(q6, bucket); @@ -719,25 +717,22 @@ insert: } /* Reassembly is complete; concatenate fragments. */ - ip6af = q6->ip6q_down; + ip6af = TAILQ_FIRST(&q6->ip6q_frags); t = m = IP6_REASS_MBUF(ip6af); - af6 = ip6af->ip6af_down; - frag6_deq(ip6af, bucket); - while (af6 != (struct ip6asfrag *)q6) { + TAILQ_REMOVE(&q6->ip6q_frags, ip6af, ip6af_tq); + while ((af6 = TAILQ_FIRST(&q6->ip6q_frags)) != NULL) { m->m_pkthdr.csum_flags &= IP6_REASS_MBUF(af6)->m_pkthdr.csum_flags; m->m_pkthdr.csum_data += IP6_REASS_MBUF(af6)->m_pkthdr.csum_data; - af6dwn = af6->ip6af_down; - frag6_deq(af6, bucket); + TAILQ_REMOVE(&q6->ip6q_frags, af6, ip6af_tq); while (t->m_next) t = t->m_next; m_adj(IP6_REASS_MBUF(af6), af6->ip6af_offset); m_demote_pkthdr(IP6_REASS_MBUF(af6)); m_cat(t, IP6_REASS_MBUF(af6)); free(af6, M_FRAG6); - af6 = af6dwn; } while (m->m_pkthdr.csum_data & 0xffff0000) @@ -753,9 +748,11 @@ insert: ip6->ip6_flow |= htonl(IPTOS_ECN_CE << 20); nxt = q6->ip6q_nxt; + TAILQ_REMOVE(head, q6, ip6q_tq); + V_ip6qb[bucket].count--; + atomic_subtract_int(&frag6_nfrags, q6->ip6q_nfrag); + if (ip6_deletefraghdr(m, offset, M_NOWAIT) != 0) { - frag6_remque(q6, bucket); - atomic_subtract_int(&frag6_nfrags, q6->ip6q_nfrag); #ifdef MAC mac_ip6q_destroy(q6); #endif @@ -769,8 +766,6 @@ insert: m_copyback(m, ip6_get_prevhdr(m, offset), sizeof(uint8_t), (caddr_t)&nxt); - frag6_remque(q6, bucket); - atomic_subtract_int(&frag6_nfrags, q6->ip6q_nfrag); #ifdef MAC mac_ip6q_reassemble(q6, m); mac_ip6q_destroy(q6); @@ -833,7 +828,8 @@ void frag6_slowtimo(void) { VNET_ITERATOR_DECL(vnet_iter); - struct ip6q *head, *q6; + struct ip6qhead *head; + struct ip6q *q6, *q6tmp; uint32_t bucket; VNET_LIST_RLOCK_NOSLEEP(); @@ -842,25 +838,13 @@ frag6_slowtimo(void) for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) { IP6QB_LOCK(bucket); head = IP6QB_HEAD(bucket); - q6 = head->ip6q_next; - if (q6 == NULL) { - /* - * XXXJTL: This should never happen. This - * should turn into an assertion. - */ - IP6QB_UNLOCK(bucket); - continue; - } - while (q6 != head) { - --q6->ip6q_ttl; - q6 = q6->ip6q_next; - if (q6->ip6q_prev->ip6q_ttl == 0) { + TAILQ_FOREACH_SAFE(q6, head, ip6q_tq, q6tmp) + if (--q6->ip6q_ttl == 0) { IP6STAT_ADD(ip6s_fragtimeout, - q6->ip6q_prev->ip6q_nfrag); + q6->ip6q_nfrag); /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ - frag6_freef(q6->ip6q_prev, bucket); + frag6_freef(q6, bucket); } - } /* * If we are over the maximum number of fragments * (due to the limit being lowered), drain off @@ -873,11 +857,10 @@ frag6_slowtimo(void) while ((V_ip6_maxfragpackets == 0 || (V_ip6_maxfragpackets > 0 && V_ip6qb[bucket].count > V_ip6_maxfragbucketsize)) && - head->ip6q_prev != head) { - IP6STAT_ADD(ip6s_fragoverflow, - q6->ip6q_prev->ip6q_nfrag); + (q6 = TAILQ_LAST(head, ip6qhead)) != NULL) { + IP6STAT_ADD(ip6s_fragoverflow, q6->ip6q_nfrag); /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ - frag6_freef(head->ip6q_prev, bucket); + frag6_freef(q6, bucket); } IP6QB_UNLOCK(bucket); } @@ -890,12 +873,11 @@ frag6_slowtimo(void) atomic_load_int(&V_frag6_nfragpackets) > (u_int)V_ip6_maxfragpackets) { IP6QB_LOCK(bucket); - head = IP6QB_HEAD(bucket); - if (head->ip6q_prev != head) { - IP6STAT_ADD(ip6s_fragoverflow, - q6->ip6q_prev->ip6q_nfrag); + q6 = TAILQ_LAST(IP6QB_HEAD(bucket), ip6qhead); + if (q6 != NULL) { + IP6STAT_ADD(ip6s_fragoverflow, q6->ip6q_nfrag); /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ - frag6_freef(head->ip6q_prev, bucket); + frag6_freef(q6, bucket); } IP6QB_UNLOCK(bucket); bucket = (bucket + 1) % IP6REASS_NHASH; @@ -930,14 +912,12 @@ frag6_change(void *tag) void frag6_init(void) { - struct ip6q *q6; uint32_t bucket; V_ip6_maxfragpackets = IP6_MAXFRAGPACKETS; frag6_set_bucketsize(); for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) { - q6 = IP6QB_HEAD(bucket); - q6->ip6q_next = q6->ip6q_prev = q6; + TAILQ_INIT(IP6QB_HEAD(bucket)); mtx_init(&V_ip6qb[bucket].lock, "ip6qlock", NULL, MTX_DEF); V_ip6qb[bucket].count = 0; } @@ -960,16 +940,15 @@ frag6_init(void) static void frag6_drain_one(void) { - struct ip6q *head; + struct ip6q *q6; uint32_t bucket; for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) { IP6QB_LOCK(bucket); - head = IP6QB_HEAD(bucket); - while (head->ip6q_next != head) { + while ((q6 = TAILQ_FIRST(IP6QB_HEAD(bucket))) != NULL) { IP6STAT_INC(ip6s_fragdropped); /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ - frag6_freef(head->ip6q_next, bucket); + frag6_freef(q6, bucket); } IP6QB_UNLOCK(bucket); } @@ -1008,60 +987,3 @@ frag6_destroy(void) } } #endif - -/* - * Put an ip fragment on a reassembly chain. - * Like insque, but pointers in middle of structure. - */ -static void -frag6_enq(struct ip6asfrag *af6, struct ip6asfrag *up6, - uint32_t bucket __unused) -{ - - IP6QB_LOCK_ASSERT(bucket); - - af6->ip6af_up = up6; - af6->ip6af_down = up6->ip6af_down; - up6->ip6af_down->ip6af_up = af6; - up6->ip6af_down = af6; -} - -/* - * To frag6_enq as remque is to insque. - */ -static void -frag6_deq(struct ip6asfrag *af6, uint32_t bucket __unused) -{ - - IP6QB_LOCK_ASSERT(bucket); - - af6->ip6af_up->ip6af_down = af6->ip6af_down; - af6->ip6af_down->ip6af_up = af6->ip6af_up; -} - -static void -frag6_insque_head(struct ip6q *new, struct ip6q *old, uint32_t bucket) -{ - - IP6QB_LOCK_ASSERT(bucket); - KASSERT(IP6QB_HEAD(bucket) == old, - ("%s: attempt to insert at head of wrong bucket" - " (bucket=%u, old=%p)", __func__, bucket, old)); - - new->ip6q_prev = old; - new->ip6q_next = old->ip6q_next; - old->ip6q_next->ip6q_prev= new; - old->ip6q_next = new; - V_ip6qb[bucket].count++; -} - -static void -frag6_remque(struct ip6q *p6, uint32_t bucket) -{ - - IP6QB_LOCK_ASSERT(bucket); - - p6->ip6q_prev->ip6q_next = p6->ip6q_next; - p6->ip6q_next->ip6q_prev = p6->ip6q_prev; - V_ip6qb[bucket].count--; -} Modified: head/sys/netinet6/ip6_var.h ============================================================================== --- head/sys/netinet6/ip6_var.h Wed Oct 23 20:39:21 2019 (r353964) +++ head/sys/netinet6/ip6_var.h Wed Oct 23 23:01:18 2019 (r353965) @@ -68,25 +68,27 @@ #include <sys/epoch.h> -struct ip6asfrag; +#ifdef _KERNEL +struct ip6asfrag; /* frag6.c */ +TAILQ_HEAD(ip6fraghead, ip6asfrag); + /* * IP6 reassembly queue structure. Each fragment * being reassembled is attached to one of these structures. */ struct ip6q { - struct ip6asfrag *ip6q_down; - struct ip6asfrag *ip6q_up; + struct ip6fraghead ip6q_frags; u_int32_t ip6q_ident; u_int8_t ip6q_nxt; u_int8_t ip6q_ecn; u_int8_t ip6q_ttl; struct in6_addr ip6q_src, ip6q_dst; - struct ip6q *ip6q_next; - struct ip6q *ip6q_prev; + TAILQ_ENTRY(ip6q) ip6q_tq; int ip6q_unfrglen; /* len of unfragmentable part */ int ip6q_nfrag; /* # of fragments */ struct label *ip6q_label; }; +#endif /* _KERNEL */ /* * IP6 reinjecting structure.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201910232301.x9NN1ITc062893>