diff options
Diffstat (limited to 'net/ipv4/ip_fragment.c')
| -rw-r--r-- | net/ipv4/ip_fragment.c | 89 | 
1 files changed, 51 insertions, 38 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index a1151b8adf3..0ad6035f636 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -77,22 +77,40 @@ struct ipq {  	struct inet_peer *peer;  }; -#define IPFRAG_ECN_CLEAR  0x01 /* one frag had INET_ECN_NOT_ECT */ -#define IPFRAG_ECN_SET_CE 0x04 /* one frag had INET_ECN_CE */ +/* RFC 3168 support : + * We want to check ECN values of all fragments, do detect invalid combinations. + * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value. + */ +#define	IPFRAG_ECN_NOT_ECT	0x01 /* one frag had ECN_NOT_ECT */ +#define	IPFRAG_ECN_ECT_1	0x02 /* one frag had ECN_ECT_1 */ +#define	IPFRAG_ECN_ECT_0	0x04 /* one frag had ECN_ECT_0 */ +#define	IPFRAG_ECN_CE		0x08 /* one frag had ECN_CE */  static inline u8 ip4_frag_ecn(u8 tos)  { -	tos = (tos & INET_ECN_MASK) + 1; -	/* -	 * After the last operation we have (in binary): -	 * INET_ECN_NOT_ECT => 001 -	 * INET_ECN_ECT_1   => 010 -	 * INET_ECN_ECT_0   => 011 -	 * INET_ECN_CE      => 100 -	 */ -	return (tos & 2) ? 0 : tos; +	return 1 << (tos & INET_ECN_MASK);  } +/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements + * Value : 0xff if frame should be dropped. + *         0 or INET_ECN_CE value, to be ORed in to final iph->tos field + */ +static const u8 ip4_frag_ecn_table[16] = { +	/* at least one fragment had CE, and others ECT_0 or ECT_1 */ +	[IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0]			= INET_ECN_CE, +	[IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1]			= INET_ECN_CE, +	[IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1]	= INET_ECN_CE, + +	/* invalid combinations : drop frame */ +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff, +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff, +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff, +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff, +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff, +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff, +	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff, +}; +  static struct inet_frags ip4_frags;  int ip_frag_nqueues(struct net *net) @@ -223,31 +241,30 @@ static void ip_expire(unsigned long arg)  	if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) {  		struct sk_buff *head = qp->q.fragments; +		const struct iphdr *iph; +		int err;  		rcu_read_lock();  		head->dev = dev_get_by_index_rcu(net, qp->iif);  		if (!head->dev)  			goto out_rcu_unlock; +		/* skb dst is stale, drop it, and perform route lookup again */ +		skb_dst_drop(head); +		iph = ip_hdr(head); +		err = ip_route_input_noref(head, iph->daddr, iph->saddr, +					   iph->tos, head->dev); +		if (err) +			goto out_rcu_unlock; +  		/* -		 * Only search router table for the head fragment, -		 * when defraging timeout at PRE_ROUTING HOOK. +		 * Only an end host needs to send an ICMP +		 * "Fragment Reassembly Timeout" message, per RFC792.  		 */ -		if (qp->user == IP_DEFRAG_CONNTRACK_IN && !skb_dst(head)) { -			const struct iphdr *iph = ip_hdr(head); -			int err = ip_route_input(head, iph->daddr, iph->saddr, -						 iph->tos, head->dev); -			if (unlikely(err)) -				goto out_rcu_unlock; - -			/* -			 * Only an end host needs to send an ICMP -			 * "Fragment Reassembly Timeout" message, per RFC792. -			 */ -			if (skb_rtable(head)->rt_type != RTN_LOCAL) -				goto out_rcu_unlock; +		if (qp->user == IP_DEFRAG_CONNTRACK_IN && +		    skb_rtable(head)->rt_type != RTN_LOCAL) +			goto out_rcu_unlock; -		}  		/* Send an ICMP "Fragment Reassembly Timeout" message. */  		icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); @@ -525,9 +542,15 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,  	int len;  	int ihlen;  	int err; +	u8 ecn;  	ipq_kill(qp); +	ecn = ip4_frag_ecn_table[qp->ecn]; +	if (unlikely(ecn == 0xff)) { +		err = -EINVAL; +		goto out_fail; +	}  	/* Make the one we just received the head. */  	if (prev) {  		head = prev->next; @@ -606,17 +629,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,  	iph = ip_hdr(head);  	iph->frag_off = 0;  	iph->tot_len = htons(len); -	/* RFC3168 5.3 Fragmentation support -	 * If one fragment had INET_ECN_NOT_ECT, -	 *	reassembled frame also has INET_ECN_NOT_ECT -	 * Elif one fragment had INET_ECN_CE -	 *	reassembled frame also has INET_ECN_CE -	 */ -	if (qp->ecn & IPFRAG_ECN_CLEAR) -		iph->tos &= ~INET_ECN_MASK; -	else if (qp->ecn & IPFRAG_ECN_SET_CE) -		iph->tos |= INET_ECN_CE; - +	iph->tos |= ecn;  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);  	qp->q.fragments = NULL;  	qp->q.fragments_tail = NULL;  |