[IPV6] MIP6: Add receiving mobility header functions through raw socket.
Like ICMPv6, mobility header is handled through raw socket. In inbound case, check only whether ICMPv6 error should be sent as a reply or not by kernel. Based on MIPL2 kernel patch. This patch was also written by: Ville Nuorvala <vnuorval@tcs.hut.fi> This patch was also written by: Antti Tuominen <anttit@tcs.hut.fi> Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org> Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
2b741653b6
commit
7be96f7628
3 changed files with 115 additions and 1 deletions
|
@ -25,6 +25,9 @@
|
||||||
#ifndef _NET_MIP6_H
|
#ifndef _NET_MIP6_H
|
||||||
#define _NET_MIP6_H
|
#define _NET_MIP6_H
|
||||||
|
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <net/sock.h>
|
||||||
|
|
||||||
#define MIP6_OPT_PAD_1 0
|
#define MIP6_OPT_PAD_1 0
|
||||||
#define MIP6_OPT_PAD_N 1
|
#define MIP6_OPT_PAD_N 1
|
||||||
|
|
||||||
|
@ -53,5 +56,6 @@ struct ip6_mh {
|
||||||
|
|
||||||
extern int mip6_init(void);
|
extern int mip6_init(void);
|
||||||
extern void mip6_fini(void);
|
extern void mip6_fini(void);
|
||||||
|
extern int mip6_mh_filter(struct sock *sk, struct sk_buff *skb);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,7 +26,10 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/ipv6.h>
|
#include <linux/ipv6.h>
|
||||||
|
#include <linux/icmpv6.h>
|
||||||
|
#include <net/sock.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6.h>
|
||||||
|
#include <net/ip6_checksum.h>
|
||||||
#include <net/xfrm.h>
|
#include <net/xfrm.h>
|
||||||
#include <net/mip6.h>
|
#include <net/mip6.h>
|
||||||
|
|
||||||
|
@ -55,6 +58,86 @@ static inline void *mip6_padn(__u8 *data, __u8 padlen)
|
||||||
return data + padlen;
|
return data + padlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void mip6_param_prob(struct sk_buff *skb, int code, int pos)
|
||||||
|
{
|
||||||
|
icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos, skb->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mip6_mh_len(int type)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case IP6_MH_TYPE_BRR:
|
||||||
|
len = 0;
|
||||||
|
break;
|
||||||
|
case IP6_MH_TYPE_HOTI:
|
||||||
|
case IP6_MH_TYPE_COTI:
|
||||||
|
case IP6_MH_TYPE_BU:
|
||||||
|
case IP6_MH_TYPE_BACK:
|
||||||
|
len = 1;
|
||||||
|
break;
|
||||||
|
case IP6_MH_TYPE_HOT:
|
||||||
|
case IP6_MH_TYPE_COT:
|
||||||
|
case IP6_MH_TYPE_BERROR:
|
||||||
|
len = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct ip6_mh *mh;
|
||||||
|
int mhlen;
|
||||||
|
|
||||||
|
if (!pskb_may_pull(skb, (skb->h.raw - skb->data) + 8) ||
|
||||||
|
!pskb_may_pull(skb, (skb->h.raw - skb->data) + ((skb->h.raw[1] + 1) << 3)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mh = (struct ip6_mh *)skb->h.raw;
|
||||||
|
|
||||||
|
if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
|
||||||
|
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n",
|
||||||
|
mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type));
|
||||||
|
mip6_param_prob(skb, 0, (&mh->ip6mh_hdrlen) - skb->nh.raw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
mhlen = (mh->ip6mh_hdrlen + 1) << 3;
|
||||||
|
|
||||||
|
if (skb->ip_summed == CHECKSUM_COMPLETE) {
|
||||||
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||||
|
if (csum_ipv6_magic(&skb->nh.ipv6h->saddr,
|
||||||
|
&skb->nh.ipv6h->daddr,
|
||||||
|
mhlen, IPPROTO_MH,
|
||||||
|
skb->csum)) {
|
||||||
|
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH hw checksum failed\n");
|
||||||
|
skb->ip_summed = CHECKSUM_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skb->ip_summed == CHECKSUM_NONE) {
|
||||||
|
if (csum_ipv6_magic(&skb->nh.ipv6h->saddr,
|
||||||
|
&skb->nh.ipv6h->daddr,
|
||||||
|
mhlen, IPPROTO_MH,
|
||||||
|
skb_checksum(skb, 0, mhlen, 0))) {
|
||||||
|
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",
|
||||||
|
NIP6(skb->nh.ipv6h->saddr),
|
||||||
|
NIP6(skb->nh.ipv6h->daddr));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mh->ip6mh_proto != IPPROTO_NONE) {
|
||||||
|
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n",
|
||||||
|
mh->ip6mh_proto);
|
||||||
|
mip6_param_prob(skb, 0, (&mh->ip6mh_proto) - skb->nh.raw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb)
|
static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct ipv6hdr *iph = skb->nh.ipv6h;
|
struct ipv6hdr *iph = skb->nh.ipv6h;
|
||||||
|
|
|
@ -50,6 +50,9 @@
|
||||||
#include <net/udp.h>
|
#include <net/udp.h>
|
||||||
#include <net/inet_common.h>
|
#include <net/inet_common.h>
|
||||||
#include <net/tcp_states.h>
|
#include <net/tcp_states.h>
|
||||||
|
#ifdef CONFIG_IPV6_MIP6
|
||||||
|
#include <net/mip6.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <net/rawv6.h>
|
#include <net/rawv6.h>
|
||||||
#include <net/xfrm.h>
|
#include <net/xfrm.h>
|
||||||
|
@ -169,8 +172,32 @@ int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
|
||||||
sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, IP6CB(skb)->iif);
|
sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, IP6CB(skb)->iif);
|
||||||
|
|
||||||
while (sk) {
|
while (sk) {
|
||||||
|
int filtered;
|
||||||
|
|
||||||
delivered = 1;
|
delivered = 1;
|
||||||
if (nexthdr != IPPROTO_ICMPV6 || !icmpv6_filter(sk, skb)) {
|
switch (nexthdr) {
|
||||||
|
case IPPROTO_ICMPV6:
|
||||||
|
filtered = icmpv6_filter(sk, skb);
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_IPV6_MIP6
|
||||||
|
case IPPROTO_MH:
|
||||||
|
/* XXX: To validate MH only once for each packet,
|
||||||
|
* this is placed here. It should be after checking
|
||||||
|
* xfrm policy, however it doesn't. The checking xfrm
|
||||||
|
* policy is placed in rawv6_rcv() because it is
|
||||||
|
* required for each socket.
|
||||||
|
*/
|
||||||
|
filtered = mip6_mh_filter(sk, skb);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
filtered = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtered < 0)
|
||||||
|
break;
|
||||||
|
if (filtered == 0) {
|
||||||
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
|
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
|
||||||
|
|
||||||
/* Not releasing hash table! */
|
/* Not releasing hash table! */
|
||||||
|
|
Loading…
Reference in a new issue