diff options
Diffstat (limited to 'net/ipv6/mip6.c')
-rw-r--r-- | net/ipv6/mip6.c | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index a8adf891fe0e..7b5f89321482 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c | |||
@@ -26,7 +26,10 @@ | |||
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/skbuff.h> | 27 | #include <linux/skbuff.h> |
28 | #include <linux/ipv6.h> | 28 | #include <linux/ipv6.h> |
29 | #include <linux/icmpv6.h> | ||
30 | #include <net/sock.h> | ||
29 | #include <net/ipv6.h> | 31 | #include <net/ipv6.h> |
32 | #include <net/ip6_checksum.h> | ||
30 | #include <net/xfrm.h> | 33 | #include <net/xfrm.h> |
31 | #include <net/mip6.h> | 34 | #include <net/mip6.h> |
32 | 35 | ||
@@ -55,6 +58,86 @@ static inline void *mip6_padn(__u8 *data, __u8 padlen) | |||
55 | return data + padlen; | 58 | return data + padlen; |
56 | } | 59 | } |
57 | 60 | ||
61 | static inline void mip6_param_prob(struct sk_buff *skb, int code, int pos) | ||
62 | { | ||
63 | icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos, skb->dev); | ||
64 | } | ||
65 | |||
66 | static int mip6_mh_len(int type) | ||
67 | { | ||
68 | int len = 0; | ||
69 | |||
70 | switch (type) { | ||
71 | case IP6_MH_TYPE_BRR: | ||
72 | len = 0; | ||
73 | break; | ||
74 | case IP6_MH_TYPE_HOTI: | ||
75 | case IP6_MH_TYPE_COTI: | ||
76 | case IP6_MH_TYPE_BU: | ||
77 | case IP6_MH_TYPE_BACK: | ||
78 | len = 1; | ||
79 | break; | ||
80 | case IP6_MH_TYPE_HOT: | ||
81 | case IP6_MH_TYPE_COT: | ||
82 | case IP6_MH_TYPE_BERROR: | ||
83 | len = 2; | ||
84 | break; | ||
85 | } | ||
86 | return len; | ||
87 | } | ||
88 | |||
89 | int mip6_mh_filter(struct sock *sk, struct sk_buff *skb) | ||
90 | { | ||
91 | struct ip6_mh *mh; | ||
92 | int mhlen; | ||
93 | |||
94 | if (!pskb_may_pull(skb, (skb->h.raw - skb->data) + 8) || | ||
95 | !pskb_may_pull(skb, (skb->h.raw - skb->data) + ((skb->h.raw[1] + 1) << 3))) | ||
96 | return -1; | ||
97 | |||
98 | mh = (struct ip6_mh *)skb->h.raw; | ||
99 | |||
100 | if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) { | ||
101 | LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n", | ||
102 | mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type)); | ||
103 | mip6_param_prob(skb, 0, (&mh->ip6mh_hdrlen) - skb->nh.raw); | ||
104 | return -1; | ||
105 | } | ||
106 | mhlen = (mh->ip6mh_hdrlen + 1) << 3; | ||
107 | |||
108 | if (skb->ip_summed == CHECKSUM_COMPLETE) { | ||
109 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
110 | if (csum_ipv6_magic(&skb->nh.ipv6h->saddr, | ||
111 | &skb->nh.ipv6h->daddr, | ||
112 | mhlen, IPPROTO_MH, | ||
113 | skb->csum)) { | ||
114 | LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH hw checksum failed\n"); | ||
115 | skb->ip_summed = CHECKSUM_NONE; | ||
116 | } | ||
117 | } | ||
118 | if (skb->ip_summed == CHECKSUM_NONE) { | ||
119 | if (csum_ipv6_magic(&skb->nh.ipv6h->saddr, | ||
120 | &skb->nh.ipv6h->daddr, | ||
121 | mhlen, IPPROTO_MH, | ||
122 | skb_checksum(skb, 0, mhlen, 0))) { | ||
123 | LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n", | ||
124 | NIP6(skb->nh.ipv6h->saddr), | ||
125 | NIP6(skb->nh.ipv6h->daddr)); | ||
126 | return -1; | ||
127 | } | ||
128 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
129 | } | ||
130 | |||
131 | if (mh->ip6mh_proto != IPPROTO_NONE) { | ||
132 | LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n", | ||
133 | mh->ip6mh_proto); | ||
134 | mip6_param_prob(skb, 0, (&mh->ip6mh_proto) - skb->nh.raw); | ||
135 | return -1; | ||
136 | } | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
58 | static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb) | 141 | static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb) |
59 | { | 142 | { |
60 | struct ipv6hdr *iph = skb->nh.ipv6h; | 143 | struct ipv6hdr *iph = skb->nh.ipv6h; |