📄 ip_gre.c
字号:
n = ntohs(icmp_hdr(skb)->un.frag.mtu); if (n < grehlen+68) return; n -= grehlen; /* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */ if (n > ntohs(eiph->tot_len)) return; rel_info = htonl(n); break; 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 = ICMP_DEST_UNREACH; rel_code = ICMP_HOST_UNREACH; break; } break; case ICMP_TIME_EXCEEDED: if (code != ICMP_EXC_TTL) return; break; } /* Prepare fake skb to feed it to icmp_send */ skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2 == NULL) return; dst_release(skb2->dst); skb2->dst = NULL; skb_pull(skb2, skb->data - (u8*)eiph); skb_reset_network_header(skb2); /* Try to guess incoming interface */ memset(&fl, 0, sizeof(fl)); fl.fl4_dst = eiph->saddr; fl.fl4_tos = RT_TOS(eiph->tos); fl.proto = IPPROTO_GRE; if (ip_route_output_key(&rt, &fl)) { kfree_skb(skb2); return; } skb2->dev = rt->u.dst.dev; /* route "incoming" packet */ if (rt->rt_flags&RTCF_LOCAL) { ip_rt_put(rt); rt = NULL; fl.fl4_dst = eiph->daddr; fl.fl4_src = eiph->saddr; fl.fl4_tos = eiph->tos; if (ip_route_output_key(&rt, &fl) || rt->u.dst.dev->type != ARPHRD_IPGRE) { ip_rt_put(rt); kfree_skb(skb2); return; } } else { ip_rt_put(rt); if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) || skb2->dst->dev->type != ARPHRD_IPGRE) { kfree_skb(skb2); return; } } /* change mtu on this route */ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { if (n > dst_mtu(skb2->dst)) { kfree_skb(skb2); return; } skb2->dst->ops->update_pmtu(skb2->dst, n); } else if (type == ICMP_TIME_EXCEEDED) { struct ip_tunnel *t = netdev_priv(skb2->dev); if (t->parms.iph.ttl) { rel_type = ICMP_DEST_UNREACH; rel_code = ICMP_HOST_UNREACH; } } icmp_send(skb2, rel_type, rel_code, rel_info); kfree_skb(skb2);#endif}static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb){ if (INET_ECN_is_ce(iph->tos)) { if (skb->protocol == htons(ETH_P_IP)) { IP_ECN_set_ce(ip_hdr(skb)); } else if (skb->protocol == htons(ETH_P_IPV6)) { IP6_ECN_set_ce(ipv6_hdr(skb)); } }}static inline u8ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb){ u8 inner = 0; if (skb->protocol == htons(ETH_P_IP)) inner = old_iph->tos; else if (skb->protocol == htons(ETH_P_IPV6)) inner = ipv6_get_dsfield((struct ipv6hdr *)old_iph); return INET_ECN_encapsulate(tos, inner);}static int ipgre_rcv(struct sk_buff *skb){ struct iphdr *iph; u8 *h; __be16 flags; __sum16 csum = 0; __be32 key = 0; u32 seqno = 0; struct ip_tunnel *tunnel; int offset = 4; if (!pskb_may_pull(skb, 16)) goto drop_nolock; iph = ip_hdr(skb); h = skb->data; flags = *(__be16*)h; if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) { /* - Version must be 0. - We do not support routing headers. */ if (flags&(GRE_VERSION|GRE_ROUTING)) goto drop_nolock; if (flags&GRE_CSUM) { switch (skb->ip_summed) { case CHECKSUM_COMPLETE: csum = csum_fold(skb->csum); if (!csum) break; /* fall through */ case CHECKSUM_NONE: skb->csum = 0; csum = __skb_checksum_complete(skb); skb->ip_summed = CHECKSUM_COMPLETE; } offset += 4; } if (flags&GRE_KEY) { key = *(__be32*)(h + offset); offset += 4; } if (flags&GRE_SEQ) { seqno = ntohl(*(__be32*)(h + offset)); offset += 4; } } read_lock(&ipgre_lock); if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) { secpath_reset(skb); skb->protocol = *(__be16*)(h + 2); /* WCCP version 1 and 2 protocol decoding. * - Change protocol to IP * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header */ if (flags == 0 && skb->protocol == htons(ETH_P_WCCP)) { skb->protocol = htons(ETH_P_IP); if ((*(h + offset) & 0xF0) != 0x40) offset += 4; } skb->mac_header = skb->network_header; __pskb_pull(skb, offset); skb_reset_network_header(skb); skb_postpull_rcsum(skb, skb_transport_header(skb), offset); skb->pkt_type = PACKET_HOST;#ifdef CONFIG_NET_IPGRE_BROADCAST if (MULTICAST(iph->daddr)) { /* Looped back packet, drop it! */ if (((struct rtable*)skb->dst)->fl.iif == 0) goto drop; tunnel->stat.multicast++; skb->pkt_type = PACKET_BROADCAST; }#endif if (((flags&GRE_CSUM) && csum) || (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) { tunnel->stat.rx_crc_errors++; tunnel->stat.rx_errors++; goto drop; } if (tunnel->parms.i_flags&GRE_SEQ) { if (!(flags&GRE_SEQ) || (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) { tunnel->stat.rx_fifo_errors++; tunnel->stat.rx_errors++; goto drop; } tunnel->i_seqno = seqno + 1; } tunnel->stat.rx_packets++; tunnel->stat.rx_bytes += skb->len; skb->dev = tunnel->dev; dst_release(skb->dst); skb->dst = NULL; nf_reset(skb); ipgre_ecn_decapsulate(iph, skb); netif_rx(skb); read_unlock(&ipgre_lock); return(0); } icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);drop: read_unlock(&ipgre_lock);drop_nolock: kfree_skb(skb); return(0);}static int ipgre_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 *old_iph = ip_hdr(skb); struct iphdr *tiph; u8 tos; __be16 df; struct rtable *rt; /* Route to the other host */ struct net_device *tdev; /* Device to other host */ struct iphdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ int gre_hlen; __be32 dst; int mtu; if (tunnel->recursion++) { tunnel->stat.collisions++; goto tx_error; } if (dev->header_ops) { gre_hlen = 0; tiph = (struct iphdr*)skb->data; } else { gre_hlen = tunnel->hlen; tiph = &tunnel->parms.iph; } if ((dst = tiph->daddr) == 0) { /* NBMA tunnel */ if (skb->dst == NULL) { tunnel->stat.tx_fifo_errors++; goto tx_error; } if (skb->protocol == htons(ETH_P_IP)) { rt = (struct rtable*)skb->dst; if ((dst = rt->rt_gateway) == 0) goto tx_error_icmp; }#ifdef CONFIG_IPV6 else if (skb->protocol == htons(ETH_P_IPV6)) { struct in6_addr *addr6; int addr_type; struct neighbour *neigh = skb->dst->neighbour; if (neigh == NULL) goto tx_error; addr6 = (struct in6_addr*)&neigh->primary_key; addr_type = ipv6_addr_type(addr6); if (addr_type == IPV6_ADDR_ANY) { addr6 = &ipv6_hdr(skb)->daddr; addr_type = ipv6_addr_type(addr6); } if ((addr_type & IPV6_ADDR_COMPATv4) == 0) goto tx_error_icmp; dst = addr6->s6_addr32[3]; }#endif else goto tx_error; } tos = tiph->tos; if (tos&1) { if (skb->protocol == htons(ETH_P_IP)) tos = old_iph->tos; tos &= ~1; } { struct flowi fl = { .oif = tunnel->parms.link, .nl_u = { .ip4_u = { .daddr = dst, .saddr = tiph->saddr, .tos = RT_TOS(tos) } }, .proto = IPPROTO_GRE }; if (ip_route_output_key(&rt, &fl)) { tunnel->stat.tx_carrier_errors++; goto tx_error; } } tdev = rt->u.dst.dev; if (tdev == dev) { ip_rt_put(rt); tunnel->stat.collisions++; goto tx_error; } df = tiph->frag_off; if (df) mtu = dst_mtu(&rt->u.dst) - tunnel->hlen; else mtu = skb->dst ? dst_mtu(skb->dst) : dev->mtu; if (skb->dst) skb->dst->ops->update_pmtu(skb->dst, mtu); if (skb->protocol == htons(ETH_P_IP)) { df |= (old_iph->frag_off&htons(IP_DF)); if ((old_iph->frag_off&htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); ip_rt_put(rt); goto tx_error; } }#ifdef CONFIG_IPV6 else if (skb->protocol == htons(ETH_P_IPV6)) { struct rt6_info *rt6 = (struct rt6_info*)skb->dst; if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) { if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) || rt6->rt6i_dst.plen == 128) { rt6->rt6i_flags |= RTF_MODIFIED; skb->dst->metrics[RTAX_MTU-1] = mtu; } } if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) { icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); ip_rt_put(rt); goto tx_error; } }#endif if (tunnel->err_count > 0) { if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) { tunnel->err_count--; dst_link_failure(skb); } else tunnel->err_count = 0; } max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen; if (skb_headroom(skb) < max_headroom || skb_shared(skb)|| (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) { ip_rt_put(rt); stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; return 0; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); dev_kfree_skb(skb); skb = new_skb; old_iph = ip_hdr(skb); } skb->transport_header = skb->network_header; skb_push(skb, gre_hlen); skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); dst_release(skb->dst); skb->dst = &rt->u.dst; /* * Push down and install the IPIP header. */ iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr) >> 2; iph->frag_off = df; iph->protocol = IPPROTO_GRE; iph->tos = ipgre_ecn_encapsulate(tos, old_iph, skb); iph->daddr = rt->rt_dst; iph->saddr = rt->rt_src; if ((iph->ttl = tiph->ttl) == 0) { if (skb->protocol == htons(ETH_P_IP)) iph->ttl = old_iph->ttl;#ifdef CONFIG_IPV6 else if (skb->protocol == htons(ETH_P_IPV6)) iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;#endif else iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT); } ((__be16*)(iph+1))[0] = tunnel->parms.o_flags; ((__be16*)(iph+1))[1] = skb->protocol; if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) { __be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4); if (tunnel->parms.o_flags&GRE_SEQ) { ++tunnel->o_seqno; *ptr = htonl(tunnel->o_seqno); ptr--; } if (tunnel->parms.o_flags&GRE_KEY) { *ptr = tunnel->parms.o_key; ptr--; } if (tunnel->parms.o_flags&GRE_CSUM) { *ptr = 0; *(__sum16*)ptr = ip_compute_csum((void*)(iph+1), skb->len - sizeof(struct iphdr)); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -