diff options
Diffstat (limited to 'net/ipv4/ip_fragment.c')
-rw-r--r-- | net/ipv4/ip_fragment.c | 36 |
1 files changed, 35 insertions, 1 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 168440834ade..a1151b8adf3c 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <linux/udp.h> | 45 | #include <linux/udp.h> |
46 | #include <linux/inet.h> | 46 | #include <linux/inet.h> |
47 | #include <linux/netfilter_ipv4.h> | 47 | #include <linux/netfilter_ipv4.h> |
48 | #include <net/inet_ecn.h> | ||
48 | 49 | ||
49 | /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 | 50 | /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 |
50 | * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c | 51 | * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c |
@@ -70,11 +71,28 @@ struct ipq { | |||
70 | __be32 daddr; | 71 | __be32 daddr; |
71 | __be16 id; | 72 | __be16 id; |
72 | u8 protocol; | 73 | u8 protocol; |
74 | u8 ecn; /* RFC3168 support */ | ||
73 | int iif; | 75 | int iif; |
74 | unsigned int rid; | 76 | unsigned int rid; |
75 | struct inet_peer *peer; | 77 | struct inet_peer *peer; |
76 | }; | 78 | }; |
77 | 79 | ||
80 | #define IPFRAG_ECN_CLEAR 0x01 /* one frag had INET_ECN_NOT_ECT */ | ||
81 | #define IPFRAG_ECN_SET_CE 0x04 /* one frag had INET_ECN_CE */ | ||
82 | |||
83 | static inline u8 ip4_frag_ecn(u8 tos) | ||
84 | { | ||
85 | tos = (tos & INET_ECN_MASK) + 1; | ||
86 | /* | ||
87 | * After the last operation we have (in binary): | ||
88 | * INET_ECN_NOT_ECT => 001 | ||
89 | * INET_ECN_ECT_1 => 010 | ||
90 | * INET_ECN_ECT_0 => 011 | ||
91 | * INET_ECN_CE => 100 | ||
92 | */ | ||
93 | return (tos & 2) ? 0 : tos; | ||
94 | } | ||
95 | |||
78 | static struct inet_frags ip4_frags; | 96 | static struct inet_frags ip4_frags; |
79 | 97 | ||
80 | int ip_frag_nqueues(struct net *net) | 98 | int ip_frag_nqueues(struct net *net) |
@@ -137,11 +155,12 @@ static void ip4_frag_init(struct inet_frag_queue *q, void *a) | |||
137 | 155 | ||
138 | qp->protocol = arg->iph->protocol; | 156 | qp->protocol = arg->iph->protocol; |
139 | qp->id = arg->iph->id; | 157 | qp->id = arg->iph->id; |
158 | qp->ecn = ip4_frag_ecn(arg->iph->tos); | ||
140 | qp->saddr = arg->iph->saddr; | 159 | qp->saddr = arg->iph->saddr; |
141 | qp->daddr = arg->iph->daddr; | 160 | qp->daddr = arg->iph->daddr; |
142 | qp->user = arg->user; | 161 | qp->user = arg->user; |
143 | qp->peer = sysctl_ipfrag_max_dist ? | 162 | qp->peer = sysctl_ipfrag_max_dist ? |
144 | inet_getpeer(arg->iph->saddr, 1) : NULL; | 163 | inet_getpeer_v4(arg->iph->saddr, 1) : NULL; |
145 | } | 164 | } |
146 | 165 | ||
147 | static __inline__ void ip4_frag_free(struct inet_frag_queue *q) | 166 | static __inline__ void ip4_frag_free(struct inet_frag_queue *q) |
@@ -316,6 +335,7 @@ static int ip_frag_reinit(struct ipq *qp) | |||
316 | qp->q.fragments = NULL; | 335 | qp->q.fragments = NULL; |
317 | qp->q.fragments_tail = NULL; | 336 | qp->q.fragments_tail = NULL; |
318 | qp->iif = 0; | 337 | qp->iif = 0; |
338 | qp->ecn = 0; | ||
319 | 339 | ||
320 | return 0; | 340 | return 0; |
321 | } | 341 | } |
@@ -328,6 +348,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
328 | int flags, offset; | 348 | int flags, offset; |
329 | int ihl, end; | 349 | int ihl, end; |
330 | int err = -ENOENT; | 350 | int err = -ENOENT; |
351 | u8 ecn; | ||
331 | 352 | ||
332 | if (qp->q.last_in & INET_FRAG_COMPLETE) | 353 | if (qp->q.last_in & INET_FRAG_COMPLETE) |
333 | goto err; | 354 | goto err; |
@@ -339,6 +360,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
339 | goto err; | 360 | goto err; |
340 | } | 361 | } |
341 | 362 | ||
363 | ecn = ip4_frag_ecn(ip_hdr(skb)->tos); | ||
342 | offset = ntohs(ip_hdr(skb)->frag_off); | 364 | offset = ntohs(ip_hdr(skb)->frag_off); |
343 | flags = offset & ~IP_OFFSET; | 365 | flags = offset & ~IP_OFFSET; |
344 | offset &= IP_OFFSET; | 366 | offset &= IP_OFFSET; |
@@ -472,6 +494,7 @@ found: | |||
472 | } | 494 | } |
473 | qp->q.stamp = skb->tstamp; | 495 | qp->q.stamp = skb->tstamp; |
474 | qp->q.meat += skb->len; | 496 | qp->q.meat += skb->len; |
497 | qp->ecn |= ecn; | ||
475 | atomic_add(skb->truesize, &qp->q.net->mem); | 498 | atomic_add(skb->truesize, &qp->q.net->mem); |
476 | if (offset == 0) | 499 | if (offset == 0) |
477 | qp->q.last_in |= INET_FRAG_FIRST_IN; | 500 | qp->q.last_in |= INET_FRAG_FIRST_IN; |
@@ -583,6 +606,17 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, | |||
583 | iph = ip_hdr(head); | 606 | iph = ip_hdr(head); |
584 | iph->frag_off = 0; | 607 | iph->frag_off = 0; |
585 | iph->tot_len = htons(len); | 608 | iph->tot_len = htons(len); |
609 | /* RFC3168 5.3 Fragmentation support | ||
610 | * If one fragment had INET_ECN_NOT_ECT, | ||
611 | * reassembled frame also has INET_ECN_NOT_ECT | ||
612 | * Elif one fragment had INET_ECN_CE | ||
613 | * reassembled frame also has INET_ECN_CE | ||
614 | */ | ||
615 | if (qp->ecn & IPFRAG_ECN_CLEAR) | ||
616 | iph->tos &= ~INET_ECN_MASK; | ||
617 | else if (qp->ecn & IPFRAG_ECN_SET_CE) | ||
618 | iph->tos |= INET_ECN_CE; | ||
619 | |||
586 | IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); | 620 | IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); |
587 | qp->q.fragments = NULL; | 621 | qp->q.fragments = NULL; |
588 | qp->q.fragments_tail = NULL; | 622 | qp->q.fragments_tail = NULL; |