ISATAP隧道处理
基礎(chǔ)的sit隧道處理請(qǐng)參考:SIT通用隧道, 此處僅涉及isatap相關(guān)部分。
netlink用戶接口
函數(shù)ipip6_newlink創(chuàng)建新的sit隧道,函數(shù)ipip6_tunnel_create創(chuàng)建新的隧道。
static int ipip6_newlink(struct net *src_net, struct net_device *dev,struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) {...if (ipip6_tunnel_locate(net, &nt->parms, 0))return -EEXIST;err = ipip6_tunnel_create(dev);if (err < 0) return err;對(duì)于ISATAP隧道,設(shè)置新建設(shè)備的IFF_ISATAP私有標(biāo)志位。
static int ipip6_tunnel_create(struct net_device *dev) {struct ip_tunnel *t = netdev_priv(dev);struct net *net = dev_net(dev);struct sit_net *sitn = net_generic(net, sit_net_id);int err;memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);memcpy(dev->broadcast, &t->parms.iph.daddr, 4);if ((__force u16)t->parms.i_flags & SIT_ISATAP)dev->priv_flags |= IFF_ISATAP;數(shù)據(jù)接收
在接收函數(shù)中,進(jìn)行欺騙報(bào)文檢測(cè),由函數(shù)packet_is_spoofed實(shí)現(xiàn)。
static int ipip6_rcv(struct sk_buff *skb) {const struct iphdr *iph = ip_hdr(skb);struct ip_tunnel *tunnel;sifindex = netif_is_l3_master(skb->dev) ? IPCB(skb)->iif : 0;tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev, iph->saddr, iph->daddr, sifindex);if (tunnel) {struct pcpu_sw_netstats *tstats;if (tunnel->parms.iph.protocol != IPPROTO_IPV6 &&tunnel->parms.iph.protocol != 0)goto out;skb->mac_header = skb->network_header;skb_reset_network_header(skb);IPCB(skb)->flags = 0;skb->dev = tunnel->dev;if (packet_is_spoofed(skb, iph, tunnel)) {tunnel->dev->stats.rx_errors++;goto out;}對(duì)于ISATAP隧道,即設(shè)置了設(shè)備私有標(biāo)志IFF_ISATAP的隧道,有函數(shù)isatap_chksrc檢測(cè)欺騙報(bào)文。
static bool packet_is_spoofed(struct sk_buff *skb,const struct iphdr *iph, struct ip_tunnel *tunnel) {const struct ipv6hdr *ipv6h;if (tunnel->dev->priv_flags & IFF_ISATAP) {if (!isatap_chksrc(skb, iph, tunnel))return true;return false;}if (tunnel->dev->flags & IFF_POINTOPOINT)return false;如果外層IPv4頭部的源地址等于配置的PRL(Potential Router List)中的某個(gè)地址,確定報(bào)文為合法報(bào)文,根據(jù)PRL地址的標(biāo)志設(shè)置skb的成員ndisc_nodetype。PRL可通過以下命令配置:
# ip tunnel add name isatap0 mode isatap local 192.168.1.100 # # ip tunnel prl dev isatap0 prl-default 192.168.1.1 # ip tunnel prl dev isatap0 prl-nodefault 192.168.1.2 # # ip tunnel show isatap0: ipv6/ip remote any local 192.168.1.100 pdr 192.168.1.1 pr 192.168.1.2 ttl inherit 6rd-prefix 2002::/16 # # sudo ip tunnel prl dev isatap0 prl-delete 192.168.1.1否則,如果源地址不屬于PRL,檢測(cè)內(nèi)部IPv6頭部的源地址是否是ISATAP格式地址,內(nèi)嵌的IPv4地址是否等于外層IPv4頭部的源地址,以及IPv6源地址是否和隧道設(shè)備配置的地址的前綴相同,以上條件都成立,則認(rèn)為是合法報(bào)文。
static int isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t) {struct ip_tunnel_prl_entry *p;int ok = 1;rcu_read_lock();p = __ipip6_tunnel_locate_prl(t, iph->saddr);if (p) {if (p->flags & PRL_DEFAULT)skb->ndisc_nodetype = NDISC_NODETYPE_DEFAULT;elseskb->ndisc_nodetype = NDISC_NODETYPE_NODEFAULT;} else {const struct in6_addr *addr6 = &ipv6_hdr(skb)->saddr;if (ipv6_addr_is_isatap(addr6) &&(addr6->s6_addr32[3] == iph->saddr) &&ipv6_chk_prefix(addr6, t->dev))skb->ndisc_nodetype = NDISC_NODETYPE_HOST;elseok = 0;}rcu_read_unlock();return ok; }ISATAP格式的IPv6地址判斷函數(shù)如下,0x02000000中的第6位(從左開始,基數(shù)0)為universal/local位,對(duì)于全局IPv4地址,值為1,否則為0。
static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) { return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); }數(shù)據(jù)發(fā)送
如下發(fā)送函數(shù),對(duì)于ISATAP隧道,其處理優(yōu)先級(jí)高于6rd和6to4等隧道,首先,IPv6報(bào)文的下一跳地址必須存在于鄰居表中;之后,由下一跳地址中取出對(duì)應(yīng)的外部要封裝的IPv4目的地址,這就要求下一跳IPv6地址必須是單播地址,而且是ISATAP格式的地址。否則,發(fā)生錯(cuò)誤。
static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) {const struct iphdr *tiph = &tunnel->parms.iph;const struct ipv6hdr *iph6 = ipv6_hdr(skb);__be32 dst = tiph->daddr;/* ISATAP (RFC4214) - must come before 6to4 */if (dev->priv_flags & IFF_ISATAP) {struct neighbour *neigh = NULL;bool do_tx_error = false;if (skb_dst(skb))neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);if (!neigh) {net_dbg_ratelimited("nexthop == NULL\n");goto tx_error;}addr6 = (const struct in6_addr *)&neigh->primary_key;addr_type = ipv6_addr_type(addr6);if ((addr_type & IPV6_ADDR_UNICAST) &&ipv6_addr_is_isatap(addr6))dst = addr6->s6_addr32[3];elsedo_tx_error = true;neigh_release(neigh);if (do_tx_error)goto tx_error;}if (!dst)dst = try_6rd(tunnel, &iph6->daddr);地址配置
對(duì)于ISATAP設(shè)備,不發(fā)送多播的RS請(qǐng)求。
static struct inet6_dev *ipv6_add_dev(struct net_device *dev) {struct inet6_dev *ndev;ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);if (!ndev)return ERR_PTR(err);if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))ndev->cnf.accept_dad = -1;#if IS_ENABLED(CONFIG_IPV6_SIT)if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {pr_info("%s: Disabled Multicast RS\n", dev->name);ndev->cnf.rtr_solicits = 0;} #endif在生成EUI64格式的接口ID時(shí),對(duì)于ISATAP隧道設(shè)備,由以下函數(shù)__ipv6_isatap_ifid實(shí)現(xiàn)。EUI的最后4字節(jié)為本地隧道IPv4地址(local),EUI的第一個(gè)字節(jié)根據(jù)地址類型,設(shè)置全局或者本地標(biāo)志位(universal/local),之后的三個(gè)字節(jié)固定為0x005EFE。
static int addrconf_ifid_sit(u8 *eui, struct net_device *dev) {if (dev->priv_flags & IFF_ISATAP)return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr);return -1; } static int __ipv6_isatap_ifid(u8 *eui, __be32 addr) {if (addr == 0)return -1;eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) ||ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) ||ipv4_is_private_172(addr) || ipv4_is_test_192(addr) ||ipv4_is_anycast_6to4(addr) || ipv4_is_private_192(addr) ||ipv4_is_test_198(addr) || ipv4_is_multicast(addr) ||ipv4_is_lbcast(addr)) ? 0x00 : 0x02;eui[1] = 0;eui[2] = 0x5E;eui[3] = 0xFE;memcpy(eui + 4, &addr, 4);return 0; } static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) {switch (dev->type) {case ARPHRD_SIT:return addrconf_ifid_sit(eui, dev);內(nèi)核版本 5.10
總結(jié)
以上是生活随笔為你收集整理的ISATAP隧道处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: isatap linux,IPv6的IS
- 下一篇: IPv6 ISATAP配置說明