--- net/ipv6/mip6.c 1969-12-31 22:00:00.000000000 -0200 +++ ../mipv6-kernel/net/ipv6/mip6.c 2003-06-05 04:57:00.000000000 -0200 @@ -0,0 +1,341 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPPROTO_MOBILITY 62 + + +struct mipv6_be +{ + u8 payload; /* Payload Protocol */ + u8 length; /* MH Length */ + u8 type; /* MH Type */ + u8 reserved; /* Reserved */ + u16 checksum; /* Checksum */ + u8 status; /* Error code */ + u8 reserved_2; + struct in6_addr home_addr; +} __attribute__ ((packed)); + +struct socket *mipv6_mh_socket = NULL; + +static int dstopts_getfrag(const void *data, struct in6_addr *addr, + char *buff, unsigned int offset, unsigned int len) +{ + memcpy(buff, data + offset, len); + return 0; +} +static __inline__ void mip6_xmit_lock(void) +{ + + + local_bh_disable(); + if (unlikely(!spin_trylock(&mipv6_mh_socket->sk->lock.slock))) + BUG(); +} + +static __inline__ void mip6_xmit_unlock(void) +{ + spin_unlock_bh(&mipv6_mh_socket->sk->lock.slock); +} + + +void mip6_send_be(struct in6_addr *daddr, + struct in6_addr *saddr, + struct in6_addr *hao_addr) +{ + struct flowi fl; + struct mipv6_be be; + struct sock *sk = mipv6_mh_socket->sk; + + memset(&fl, 0, sizeof(fl)); + fl.proto = IPPROTO_MOBILITY; + ipv6_addr_copy(&fl.fl6_dst, daddr); + ipv6_addr_copy(&fl.fl6_src, saddr); + fl.fl6_flowlabel = 0; + fl.oif = sk->bound_dev_if; + + memset(&be, 0, sizeof(be)); + be.payload = NEXTHDR_NONE; + be.length = 2; + be.type = 7; + ipv6_addr_copy(&be.home_addr, hao_addr); + be.status = 1; /* Home address option without binding */ + + mip6_xmit_lock(); + ip6_build_xmit(sk, dstopts_getfrag, &be, &fl, sizeof(be), NULL, 255, + MSG_DONTWAIT); + mip6_xmit_unlock(); +} +/* TODO: Move the home address option / BCE check to tcp/udp/raw + * processing so cached route in socket can be used + * to avoid route lookup + */ +int mip6_hao_check(struct sk_buff *skb, u8 nexthdr) +{ + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + struct in6_addr *coaddr; + struct rt6_info *rt; + /* Home address option in mobility header messages is checked + by userspace mipv6 daemon */ + + if (!opt || !opt->dst_nofrag || nexthdr == IPPROTO_MOBILITY) + return 0; + if (opt && opt->dst_nofrag) { + rt = rt6_lookup(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, 0, 0); + if (rt) { + if (rt->binding.flags & MIPV6_F_BCE) { + dst_release(&rt->u.dst); + return 0; + } + else + dst_release(&rt->u.dst); + } + coaddr = (struct in6_addr *)((u8 *)skb->nh.raw + opt->dst_nofrag); + mip6_send_be(coaddr, &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr); + return -1; + } +} + +/** + * mipv6_append_rt2hdr - Add Type 2 Routing Header + * @rt: buffer for new routing header + * @addr: intermediate hop address + * + * Adds a Routing Header Type 2 in a packet. Stores newly created + * routing header in buffer @rt. Type 2 RT only carries one address, + * so there is no need to process old routing header. @rt must have + * allocated space for 24 bytes. + **/ +void mipv6_append_rt2hdr(struct rt2_hdr *rt, struct in6_addr *addr) +{ + struct rt2_hdr *rt2 = (struct rt2_hdr *)rt; + + memset(rt2, 0, sizeof(*rt2)); + rt2->rt_hdr.type = 2; + rt2->rt_hdr.hdrlen = 2; + rt2->rt_hdr.segments_left = 1; + ipv6_addr_copy(&rt2->addr, addr); +} + +struct mipv6_padn +{ + __u8 type; + __u8 length; + __u8 data[0]; +} __attribute__ ((packed)); + +/* + * Add Pad1 or PadN option to data + */ +int mipv6_add_pad(u8 *data, int n) +{ + struct mipv6_padn *padn; + + if (n <= 0) return 0; + if (n == 1) { + *data = MIPV6_OPT_PAD1; + return 1; + } + padn = (struct mipv6_padn *)data; + padn->type = MIPV6_OPT_PADN; + padn->length = n - 2; + memset(padn->data, 0, n - 2); + return n; +} + +/** + * mipv6_append_home_addr - Add Home Address Option + * @opt: buffer for Home Address Option + * @offset: offset from beginning of @opt + * @addr: address for HAO + * + * Adds a Home Address Option to a packet. Option is stored in + * @offset from beginning of @opt. The option is created but the + * original source address in IPv6 header is left intact. The source + * address will be changed from home address to CoA after the checksum + * has been calculated in getfrag. Padding is done automatically, and + * @opt must have allocated space for both actual option and pad. + * Returns offset from @opt to end of options. + **/ +int mipv6_append_home_addr(u8 *opt, struct in6_addr *addr) +{ + int pad; + struct ipv6_dstopt_homeaddr *ho; + int offset = sizeof(struct ipv6_opt_hdr); + + pad = (6 - offset) & 7; + mipv6_add_pad(opt + offset, pad); + + ho = (struct ipv6_dstopt_homeaddr *)(opt + offset + pad); + ho->type = IPV6_TLV_HOMEADDR; + ho->length = sizeof(*ho) - 2; + ipv6_addr_copy(&ho->addr, addr); + + return offset + pad + sizeof(*ho); +} + + +static int get_offset(u8 *packet, u32 packet_len, u8 *nexthdr, int *offset_prevhdr) +{ + u16 offset = sizeof(struct ipv6hdr); + struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(packet + offset); + u8 nextnexthdr; + + *nexthdr = ((struct ipv6hdr*)packet)->nexthdr; + + while (offset + 1 < packet_len) { + + switch (*nexthdr) { + + case NEXTHDR_HOP: + case NEXTHDR_ROUTING: + *offset_prevhdr = offset; + offset += ipv6_optlen(exthdr); + *nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(packet + offset); + break; + + case NEXTHDR_DEST: + nextnexthdr = + ((struct ipv6_opt_hdr*)(packet + offset + ipv6_optlen(exthdr)))->nexthdr; + /* XXX We know the option is inner dest opt + with next next header check. */ + if (nextnexthdr != NEXTHDR_HOP && + nextnexthdr != NEXTHDR_ROUTING && + nextnexthdr != NEXTHDR_DEST) { + return offset; + } + *offset_prevhdr = offset; + offset += ipv6_optlen(exthdr); + *nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(packet + offset); + break; + + default : + return offset; + } + } + + return offset; +} + + +int mip6_output(struct sk_buff *skb) + +{ + struct ipv6hdr *iph = NULL, *top_iph; + struct dst_entry *dst = skb->dst; + struct ipv6_opt_hdr *prevhdr = NULL; + struct rt6_info *rt = (struct rt6_info *)skb->dst; + u8 nexthdr; + int offset_prevhdr = 0; + int hdr_len = get_offset(skb->nh.raw, skb->len, &nexthdr, &offset_prevhdr); + int len, err = 0; + + if (nexthdr == IPPROTO_MOBILITY) /* No exthdrs for MH */ + goto out; + + /* First, if the skb is not checksummed, complete checksum. */ + if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) { + err = -EINVAL; + goto error; + } + iph = kmalloc(hdr_len, GFP_ATOMIC); + + if (!iph) { + err = -ENOMEM; + goto error; + } + + memcpy(iph, skb->nh.raw, hdr_len); + __skb_pull(skb, hdr_len); + + /* TODO: Is this correct ? */ + if ((err = skb_cow(skb, mip6_hdrlen(rt->binding.flags)) != 0)) + goto error; + if (rt->binding.flags & MIPV6_F_BULE) { + struct ipv6_opt_hdr *dstopt; + dstopt = (struct ipv6_opt_hdr *)skb_push(skb, sizeof(struct ipv6_dstopt_homeaddr) + 6); + dstopt->nexthdr = nexthdr; + len = mipv6_append_home_addr((u8 *)dstopt, &iph->saddr); + dstopt->hdrlen = (len >> 3) - 1; + ipv6_addr_copy(&iph->saddr, &rt->binding.lcoa); + skb->h.raw = (unsigned char *)dstopt; + nexthdr = IPPROTO_DSTOPTS; + + } + if (rt->binding.flags & MIPV6_F_BCE) { + struct rt2_hdr *rt2; + rt2 = (struct rt2_hdr *)skb_push(skb, sizeof(struct rt2_hdr)); + skb->h.raw = (unsigned char *)rt2; + mipv6_append_rt2hdr(rt2, &iph->daddr); + ipv6_addr_copy(&iph->daddr, &rt->binding.rcoa); + rt2->rt_hdr.nexthdr = nexthdr; + nexthdr = IPPROTO_ROUTING; + } + + top_iph = (struct ipv6hdr *)skb_push(skb, hdr_len); + memcpy(top_iph, iph, hdr_len); + skb->nh.raw = skb->data; + kfree(iph); + top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + if (offset_prevhdr) { + prevhdr = (struct ipv6_opt_hdr *)((int *)top_iph + offset_prevhdr); + prevhdr->nexthdr = nexthdr; + + } else { + top_iph->nexthdr = nexthdr; + } + + out: + if ((skb->dst = dst_pop(dst)) == NULL) { + err = -EHOSTUNREACH; + goto error; + } + + return NET_XMIT_BYPASS; + error: + kfree_skb(skb); + return err; +} + +int mip6_init(void) +{ + mipv6_mh_socket = sock_alloc(); + mipv6_mh_socket->type = SOCK_RAW; + struct sock *sk; + int err; + + if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_MOBILITY, + &mipv6_mh_socket)) < 0) { + printk(KERN_ERR + "Failed to initialize the MIP6 MH control socket (err %d).\n", + err); + sock_release(mipv6_mh_socket); + mipv6_mh_socket = NULL; /* for safety */ + return err; + } + + sk = mipv6_mh_socket->sk; + sk->allocation = GFP_ATOMIC; + sk->sndbuf = SK_WMEM_MAX; + sk->prot->unhash(sk); + return 0; +} + +void mip6_cleanup(void) +{ + if (mipv6_mh_socket) sock_release(mipv6_mh_socket); + mipv6_mh_socket = NULL; /* For safety. */ +} + + +MODULE_LICENSE("GPL"); --- net/ipv6/route.c 2003-06-05 08:48:57.000000000 -0200 +++ ../mipv6-kernel/net/ipv6/route.c 2003-06-05 06:37:59.000000000 -0200 @@ -52,7 +52,7 @@ #include #include #include - +#include #include #ifdef CONFIG_SYSCTL @@ -336,7 +336,7 @@ return err; } -/* No rt6_lock! If COW failed, the function returns dead route entry +/* No rt6_lock! If COW faild, the function returns dead route entry with dst->error set to errno value. */ @@ -363,12 +363,8 @@ rt->u.dst.flags |= DST_HOST; #ifdef CONFIG_IPV6_SUBTREES - if (rt->rt6i_src.plen && saddr) { - ipv6_addr_copy(&rt->rt6i_src.addr, saddr); - rt->rt6i_src.plen = 128; - } + rt->rt6i_src.plen = ort->rt6i_src.plen; #endif - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); dst_hold(&rt->u.dst); @@ -885,7 +881,7 @@ struct rt6_info *rt, *nrt; /* Locate old route to this destination. */ - rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); + rt = rt6_lookup(dest, saddr, neigh->dev->ifindex, 1); if (rt == NULL) return; @@ -1052,6 +1048,9 @@ nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; +#ifdef CONFIG_IPV6_SUBTREES + nrt->rt6i_src.plen = rt->rt6i_src.plen; +#endif ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); nrt->rt6i_dst.plen = 128; nrt->u.dst.flags |= DST_HOST; @@ -1162,7 +1161,107 @@ } read_unlock_bh(&rt6_lock); } +/* TODO: Move struct definition + * to a header file under include/linux +*/ +struct mipv6_info_user +{ + struct mip6_info bind; + unsigned long expires; + struct in6_addr src; + struct in6_addr dst; +}; + +/* Adds mip6 related info and a stacked dst entry to the new cached route. + */ +static void fill_mip6_rt(struct rt6_info *mip6rt, struct rt6_info *coart, struct mip6_info *bind) +{ + mip6rt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; + mip6rt->u.dst.flags = DST_HOST; + mip6rt->u.dst.header_len = mip6_hdrlen(mip6rt->binding.flags); + mip6rt->u.dst.metrics[RTAX_MTU-1] = coart->u.dst.metrics[RTAX_MTU-1] - + mip6rt->u.dst.header_len; + mip6rt->u.dst.metrics[RTAX_ADVMSS-1] = max_t(unsigned int, dst_pmtu(&mip6rt->u.dst) - 60, ip6_rt_min_advmss); + if (mip6rt->u.dst.metrics[RTAX_ADVMSS-1] > 65535-20) + mip6rt->u.dst.metrics[RTAX_ADVMSS-1] = 65535; + mip6rt->u.dst.child = dst_clone(&coart->u.dst); /* Is this correct ? */ + memcpy(&mip6rt->binding, bind, sizeof(bind)); + mip6rt->u.dst.output = mip6_output; +} +/* Add mipv6 information to a new cache route entry. + * Mostly copied code from rt6_pmtu_discovery + */ +int ip6_add_miproute(struct mipv6_info_user *mipinfo) +{ + /* First look up the coa route */ + struct rt6_info *rt, *mip6rt, *coart = NULL; + int err = 0; + + if ((rt = (struct rt6_info *)rt6_lookup(&mipinfo->dst, &mipinfo->src, 0, 0)) == NULL) { + return -ENOENT; + } + + /* + * Delete old host route before adding new one. TODO: Could we just modify the existing cache + * route after locking the routing table ? + */ + if (rt->rt6i_flags & RTF_CACHE) { + ip6_del_rt(rt, NULL, NULL); + rt = NULL; + } + + if ((coart = rt6_lookup(&mipinfo->bind.rcoa, &mipinfo->bind.lcoa, 0, 0)) == NULL) { + err = -NOENT; + goto out; + } + /* Network route. + Two cases are possible: + 1. It is connected route. Action: COW + 2. It is gatewayed route or NONEXTHOP route. Action: clone it. + */ + if (!coart->rt6i_nexthop && !(coart->rt6i_flags & RTF_NONEXTHOP)) { + mip6rt = rt6_cow(coart, &mipinfo->dst, &mipinfo->src); + if (!mip6rt->u.dst.error) { + mip6rt->u.dst.metrics[RTAX_MTU-1] = coart->u.dst.metrics[RTAX_MTU-1]; + dst_set_expires(&mip6rt->u.dst, HZ*mipinfo->expires); + fill_mip6_rt(mip6rt, coart, &mipinfo->bind); + dst_release(&mip6rt->u.dst); + } + } else { + + mip6rt = ip6_rt_copy(coart); + ipv6_addr_copy(&mip6rt->rt6i_dst.addr, &mipinfo->dst); + +#ifdef CONFIG_IPV6_SUBTREES + ipv6_addr_copy(&mip6rt->rt6i_src.addr, &mipinfo->src); + mip6rt->rt6i_src.plen = 128; +#endif + mip6rt->rt6i_dst.plen = 128; + mip6rt->u.dst.flags |= DST_HOST; + mip6rt->rt6i_nexthop = neigh_clone(coart->rt6i_nexthop); + dst_set_expires(&mip6rt->u.dst, HZ*mipinfo->expires); + mip6rt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES; + mip6rt->u.dst.metrics[RTAX_MTU-1] = coart->u.dst.metrics[RTAX_MTU-1]; + fill_mip6_rt(mip6rt, coart, &mipinfo->bind); + rt6_ins(mip6rt, NULL, NULL); + } + out: + if (coart) dst_release(&coart->u.dst); + if (rt) dst_release(&rt->u.dst); + return err; +} + +static int add_mip6_binding(void *arg) +{ + + struct mipv6_info_user mip; + if (copy_from_user(&mip, arg, sizeof(mip))) { + return -EINVAL; + } + + return ip6_add_miproute(&mip); +} int ipv6_route_ioctl(unsigned int cmd, void *arg) { struct in6_rtmsg rtmsg; @@ -1192,9 +1291,18 @@ rtnl_unlock(); return err; + case SIOCADDMIPINFO: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + rtnl_lock(); + err = add_mip6_binding(arg); + rtnl_unlock(); + return err; }; - return -EINVAL; + + + return -EINVAL; } /* @@ -1786,12 +1894,11 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v) { - seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", + seq_printf(seq, "%04x %04x %04x %04x %04x %04x\n", rt6_stats.fib_nodes, rt6_stats.fib_route_nodes, rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries, rt6_stats.fib_rt_cache, - atomic_read(&ip6_dst_ops.entries), - rt6_stats.fib_discarded_routes); + atomic_read(&ip6_dst_ops.entries)); return 0; } --- net/ipv6/af_inet6.c 2003-06-05 08:48:57.000000000 -0200 +++ ../mipv6-kernel/net/ipv6/af_inet6.c 2003-06-03 10:11:38.000000000 -0200 @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -310,7 +311,7 @@ } else { if (addr_type != IPV6_ADDR_ANY) { /* ipv4 addr of the socket is invalid. Only the - * unspecified and mapped address have a v4 equivalent. + * unpecified and mapped address have a v4 equivalent. */ v4addr = LOOPBACK4_IPV6; if (!(addr_type & IPV6_ADDR_MULTICAST)) { @@ -475,7 +476,7 @@ case SIOCADDRT: case SIOCDELRT: - + case SIOCADDMIPINFO: return(ipv6_route_ioctl(cmd,(void *)arg)); case SIOCSIFADDR: @@ -780,6 +781,14 @@ err = ndisc_init(&inet6_family_ops); if (err) goto ndisc_fail; +#ifdef CONFIG_IPV6_TUNNEL + err = ip6_tunnel_init(); + if (err) + goto ip6_tunnel_fail; +#endif + err = mip6_init(); + if (err) + goto mip6_fail; err = igmp6_init(&inet6_family_ops); if (err) goto igmp_fail; @@ -816,7 +825,6 @@ /* Init v6 transport protocols. */ udpv6_init(); tcpv6_init(); - return 0; #ifdef CONFIG_PROC_FS @@ -834,6 +842,12 @@ igmp6_cleanup(); #endif igmp_fail: + mip6_cleanup(); +mip6_fail: +#ifdef CONFIG_IPV6_TUNNEL + ip6_tunnel_cleanup(); +ip6_tunnel_fail: +#endif ndisc_cleanup(); ndisc_fail: icmpv6_cleanup(); @@ -869,6 +883,10 @@ ip6_route_cleanup(); ipv6_packet_cleanup(); igmp6_cleanup(); + mip6_cleanup(); +#ifdef CONFIG_IPV6_TUNNEL + ip6_tunnel_cleanup(); +#endif ndisc_cleanup(); icmpv6_cleanup(); #ifdef CONFIG_SYSCTL --- include/net/mipv6.h 1969-12-31 22:00:00.000000000 -0200 +++ ../mipv6-kernel/include/net/mipv6.h 2003-06-05 06:21:37.000000000 -0200 @@ -0,0 +1,53 @@ +/* mipv6.h - Mobile IPv6 kernel support */ + +#ifndef _NET_MIPV6_H +#define _NET_MIPV6_H + +#define MIPV6_F_BULE 0x1 +#define MIPV6_F_BCE 0x2 +#define MIPV6_OPT_PAD1 0x00 +#define MIPV6_OPT_PADN 0x01 +/** + * NIPV6ADDR - macro for IPv6 addresses + * @addr: Network byte order IPv6 address + * + * Macro for printing IPv6 addresses. Used in conjunction with + * printk() or derivatives (such as DEBUG macro). + **/ +#define NIPV6ADDR(addr) \ + ntohs(((u16 *)addr)[0]), \ + ntohs(((u16 *)addr)[1]), \ + ntohs(((u16 *)addr)[2]), \ + ntohs(((u16 *)addr)[3]), \ + ntohs(((u16 *)addr)[4]), \ + ntohs(((u16 *)addr)[5]), \ + ntohs(((u16 *)addr)[6]), \ + ntohs(((u16 *)addr)[7]) + +struct ipv6_dstopt_homeaddr +{ + __u8 type; /* type-code for option */ + __u8 length; /* option length */ + struct in6_addr addr; /* home address */ +} __attribute__ ((packed)); +static inline int mip6_hdrlen(int flags) +{ + int miphdrlen = 0; + + if (flags & MIPV6_F_BULE) + miphdrlen = sizeof(struct ipv6_dstopt_homeaddr) + 6; + if (flags & MIPV6_F_BCE) + miphdrlen += sizeof(struct rt2_hdr); + return miphdrlen; +} +int mip6_output(struct sk_buff *skb); +struct ipv6_txoptions * +mipv6_modify_txoptions(struct sock *sk, + struct ipv6_txoptions *old_opt, struct flowi *fl, + struct dst_entry **dst); + +int mip6_hao_check(struct sk_buff *skb, u8 nexthdr); +int mip6_init(void); +void mip6_cleanup(void); + +#endif /* _NET_MIPV6_H */ --- include/net/ip6_fib.h 2003-06-05 08:48:46.000000000 -0200 +++ ../mipv6-kernel/include/net/ip6_fib.h 2003-06-03 10:11:19.000000000 -0200 @@ -50,6 +50,13 @@ int plen; }; +struct mip6_info +{ + struct in6_addr lcoa; + struct in6_addr rcoa; + u32 flags; +}; + struct rt6_info { union { @@ -71,8 +78,9 @@ struct rt6key rt6i_dst; struct rt6key rt6i_src; - + u8 rt6i_protocol; + struct mip6_info binding; }; struct fib6_walker_t @@ -111,10 +119,9 @@ struct rt6_statistics { __u32 fib_nodes; __u32 fib_route_nodes; - __u32 fib_rt_alloc; /* permanent routes */ + __u32 fib_rt_alloc; /* permanet routes */ __u32 fib_rt_entries; /* rt entries in table */ __u32 fib_rt_cache; /* cache routes */ - __u32 fib_discarded_routes; }; #define RTN_TL_ROOT 0x0001