📄 sit.c
字号:
/* * IPv6 over IPv4 tunnel device - Simple Internet Transition (SIT) * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * * $Id: sit.c,v 1.53 2001/09/25 05:09:53 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Changes: * Roger Venning <r.venning@telstra.com>: 6to4 support * Nate Thompson <nate@thebog.net>: 6to4 support */#include <linux/module.h>#include <linux/capability.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/icmp.h>#include <asm/uaccess.h>#include <linux/init.h>#include <linux/netfilter_ipv4.h>#include <linux/if_ether.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/transp_v6.h>#include <net/ip6_fib.h>#include <net/ip6_route.h>#include <net/ndisc.h>#include <net/addrconf.h>#include <net/ip.h>#include <net/udp.h>#include <net/icmp.h>#include <net/ipip.h>#include <net/inet_ecn.h>#include <net/xfrm.h>#include <net/dsfield.h>/* This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c For comments look at net/ipv4/ip_gre.c --ANK */#define HASH_SIZE 16#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)static int ipip6_fb_tunnel_init(struct net_device *dev);static int ipip6_tunnel_init(struct net_device *dev);static void ipip6_tunnel_setup(struct net_device *dev);static struct net_device *ipip6_fb_tunnel_dev;static struct ip_tunnel *tunnels_r_l[HASH_SIZE];static struct ip_tunnel *tunnels_r[HASH_SIZE];static struct ip_tunnel *tunnels_l[HASH_SIZE];static struct ip_tunnel *tunnels_wc[1];static struct ip_tunnel **tunnels[4] = { tunnels_wc, tunnels_l, tunnels_r, tunnels_r_l };static DEFINE_RWLOCK(ipip6_lock);static struct ip_tunnel * ipip6_tunnel_lookup(__be32 remote, __be32 local){ unsigned h0 = HASH(remote); unsigned h1 = HASH(local); struct ip_tunnel *t; for (t = tunnels_r_l[h0^h1]; t; t = t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; } for (t = tunnels_r[h0]; t; t = t->next) { if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; } for (t = tunnels_l[h1]; t; t = t->next) { if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) return t; } if ((t = tunnels_wc[0]) != NULL && (t->dev->flags&IFF_UP)) return t; return NULL;}static struct ip_tunnel **__ipip6_bucket(struct ip_tunnel_parm *parms){ __be32 remote = parms->iph.daddr; __be32 local = parms->iph.saddr; unsigned h = 0; int prio = 0; if (remote) { prio |= 2; h ^= HASH(remote); } if (local) { prio |= 1; h ^= HASH(local); } return &tunnels[prio][h];}static inline struct ip_tunnel **ipip6_bucket(struct ip_tunnel *t){ return __ipip6_bucket(&t->parms);}static void ipip6_tunnel_unlink(struct ip_tunnel *t){ struct ip_tunnel **tp; for (tp = ipip6_bucket(t); *tp; tp = &(*tp)->next) { if (t == *tp) { write_lock_bh(&ipip6_lock); *tp = t->next; write_unlock_bh(&ipip6_lock); break; } }}static void ipip6_tunnel_link(struct ip_tunnel *t){ struct ip_tunnel **tp = ipip6_bucket(t); t->next = *tp; write_lock_bh(&ipip6_lock); *tp = t; write_unlock_bh(&ipip6_lock);}static struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int create){ __be32 remote = parms->iph.daddr; __be32 local = parms->iph.saddr; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; char name[IFNAMSIZ]; for (tp = __ipip6_bucket(parms); (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) return t; } if (!create) goto failed; if (parms->name[0]) strlcpy(name, parms->name, IFNAMSIZ); else { int i; for (i=1; i<100; i++) { sprintf(name, "sit%d", i); if (__dev_get_by_name(&init_net, name) == NULL) break; } if (i==100) goto failed; } dev = alloc_netdev(sizeof(*t), name, ipip6_tunnel_setup); if (dev == NULL) return NULL; nt = netdev_priv(dev); dev->init = ipip6_tunnel_init; nt->parms = *parms; if (register_netdevice(dev) < 0) { free_netdev(dev); goto failed; } dev_hold(dev); ipip6_tunnel_link(nt); return nt;failed: return NULL;}static void ipip6_tunnel_uninit(struct net_device *dev){ if (dev == ipip6_fb_tunnel_dev) { write_lock_bh(&ipip6_lock); tunnels_wc[0] = NULL; write_unlock_bh(&ipip6_lock); dev_put(dev); } else { ipip6_tunnel_unlink(netdev_priv(dev)); dev_put(dev); }}static int ipip6_err(struct sk_buff *skb, u32 info){#ifndef I_WISH_WORLD_WERE_PERFECT/* It is not :-( All the routers (except for Linux) return only 8 bytes of packet payload. It means, that precise relaying of ICMP in the real Internet is absolutely infeasible. */ struct iphdr *iph = (struct iphdr*)skb->data; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; struct ip_tunnel *t; int err; switch (type) { default: case ICMP_PARAMETERPROB: return 0; case ICMP_DEST_UNREACH: switch (code) { case ICMP_SR_FAILED: case ICMP_PORT_UNREACH: /* Impossible event. */ return 0; case ICMP_FRAG_NEEDED: /* Soft state for pmtu is maintained by IP core. */ return 0; default: /* All others are translated to HOST_UNREACH. rfc2003 contains "deep thoughts" about NET_UNREACH, I believe they are just ether pollution. --ANK */ break; } break; case ICMP_TIME_EXCEEDED: if (code != ICMP_EXC_TTL) return 0; break; } err = -ENOENT; read_lock(&ipip6_lock); t = ipip6_tunnel_lookup(iph->daddr, iph->saddr); if (t == NULL || t->parms.iph.daddr == 0) goto out; err = 0; if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) goto out; if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO) t->err_count++; else t->err_count = 1; t->err_time = jiffies;out: read_unlock(&ipip6_lock); return err;#else struct iphdr *iph = (struct iphdr*)dp; int hlen = iph->ihl<<2; struct ipv6hdr *iph6; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; int rel_type = 0; int rel_code = 0; int rel_info = 0; struct sk_buff *skb2; struct rt6_info *rt6i; if (len < hlen + sizeof(struct ipv6hdr)) return; iph6 = (struct ipv6hdr*)(dp + hlen); switch (type) { default: return; case ICMP_PARAMETERPROB: if (icmp_hdr(skb)->un.gateway < hlen) return; /* So... This guy found something strange INSIDE encapsulated packet. Well, he is fool, but what can we do ? */ rel_type = ICMPV6_PARAMPROB; rel_info = icmp_hdr(skb)->un.gateway - hlen; break; case ICMP_DEST_UNREACH: switch (code) { case ICMP_SR_FAILED: case ICMP_PORT_UNREACH: /* Impossible event. */ return; case ICMP_FRAG_NEEDED: /* Too complicated case ... */ return; default: /* All others are translated to HOST_UNREACH. rfc2003 contains "deep thoughts" about NET_UNREACH, I believe, it is just ether pollution. --ANK */ rel_type = ICMPV6_DEST_UNREACH; rel_code = ICMPV6_ADDR_UNREACH; break; } break; case ICMP_TIME_EXCEEDED: if (code != ICMP_EXC_TTL) return; rel_type = ICMPV6_TIME_EXCEED; rel_code = ICMPV6_EXC_HOPLIMIT; break; } /* Prepare fake skb to feed it to icmpv6_send */ skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2 == NULL) return 0; dst_release(skb2->dst); skb2->dst = NULL; skb_pull(skb2, skb->data - (u8*)iph6); skb_reset_network_header(skb2); /* Try to guess incoming interface */ rt6i = rt6_lookup(&iph6->saddr, NULL, NULL, 0); if (rt6i && rt6i->rt6i_dev) { skb2->dev = rt6i->rt6i_dev; rt6i = rt6_lookup(&iph6->daddr, &iph6->saddr, NULL, 0); if (rt6i && rt6i->rt6i_dev && rt6i->rt6i_dev->type == ARPHRD_SIT) { struct ip_tunnel *t = netdev_priv(rt6i->rt6i_dev); if (rel_type == ICMPV6_TIME_EXCEED && t->parms.iph.ttl) { rel_type = ICMPV6_DEST_UNREACH; rel_code = ICMPV6_ADDR_UNREACH; } icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev); } } kfree_skb(skb2); return 0;#endif}static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb){ if (INET_ECN_is_ce(iph->tos)) IP6_ECN_set_ce(ipv6_hdr(skb));}static int ipip6_rcv(struct sk_buff *skb){ struct iphdr *iph; struct ip_tunnel *tunnel; if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; iph = ip_hdr(skb); read_lock(&ipip6_lock); if ((tunnel = ipip6_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) { secpath_reset(skb); skb->mac_header = skb->network_header; skb_reset_network_header(skb); IPCB(skb)->flags = 0; skb->protocol = htons(ETH_P_IPV6); skb->pkt_type = PACKET_HOST; tunnel->stat.rx_packets++; tunnel->stat.rx_bytes += skb->len; skb->dev = tunnel->dev; dst_release(skb->dst); skb->dst = NULL; nf_reset(skb); ipip6_ecn_decapsulate(iph, skb); netif_rx(skb); read_unlock(&ipip6_lock); return 0; } icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); kfree_skb(skb); read_unlock(&ipip6_lock);out: return 0;}/* Returns the embedded IPv4 address if the IPv6 address comes from 6to4 (RFC 3056) addr space */static inline __be32 try_6to4(struct in6_addr *v6dst){ __be32 dst = 0; if (v6dst->s6_addr16[0] == htons(0x2002)) { /* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */ memcpy(&dst, &v6dst->s6_addr16[1], 4); } return dst;}/* * This function assumes it is being called from dev_queue_xmit() * and that skb is filled properly by that function. */static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev){ struct ip_tunnel *tunnel = netdev_priv(dev); struct net_device_stats *stats = &tunnel->stat; struct iphdr *tiph = &tunnel->parms.iph; struct ipv6hdr *iph6 = ipv6_hdr(skb); u8 tos = tunnel->parms.iph.tos; struct rtable *rt; /* Route to the other host */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -