Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipvs: strip gre tunnel headers from icmp errors

Recognize GRE tunnels in received ICMP errors and
properly strip the tunnel headers.

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Julian Anastasov and committed by
Pablo Neira Ayuso
6aedd14b ad49d86e

+42 -4
+42 -4
net/netfilter/ipvs/ip_vs_core.c
··· 35 35 #include <net/udp.h> 36 36 #include <net/icmp.h> /* for icmp_send */ 37 37 #include <net/gue.h> 38 + #include <net/gre.h> 38 39 #include <net/route.h> 39 40 #include <net/ip6_checksum.h> 40 41 #include <net/netns/generic.h> /* net_generic() */ ··· 1611 1610 return 0; 1612 1611 } 1613 1612 1613 + /* Check the GRE tunnel and return its header length */ 1614 + static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, 1615 + unsigned int offset, __u16 af, 1616 + const union nf_inet_addr *daddr, __u8 *proto) 1617 + { 1618 + struct gre_base_hdr _greh, *greh; 1619 + struct ip_vs_dest *dest; 1620 + 1621 + greh = skb_header_pointer(skb, offset, sizeof(_greh), &_greh); 1622 + if (!greh) 1623 + goto unk; 1624 + dest = ip_vs_find_tunnel(ipvs, af, daddr, 0); 1625 + if (!dest) 1626 + goto unk; 1627 + if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { 1628 + __be16 type; 1629 + 1630 + /* Only support version 0 and C (csum) */ 1631 + if ((greh->flags & ~GRE_CSUM) != 0) 1632 + goto unk; 1633 + type = greh->protocol; 1634 + /* Later we can support also IPPROTO_IPV6 */ 1635 + if (type != htons(ETH_P_IP)) 1636 + goto unk; 1637 + *proto = IPPROTO_IPIP; 1638 + return gre_calc_hlen(gre_flags_to_tnl_flags(greh->flags)); 1639 + } 1640 + 1641 + unk: 1642 + return 0; 1643 + } 1644 + 1614 1645 /* 1615 1646 * Handle ICMP messages in the outside-to-inside direction (incoming). 1616 1647 * Find any that might be relevant, check against existing connections, ··· 1722 1689 if (cih == NULL) 1723 1690 return NF_ACCEPT; /* The packet looks wrong, ignore */ 1724 1691 ipip = true; 1725 - } else if (cih->protocol == IPPROTO_UDP && /* Can be UDP encap */ 1692 + } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ 1693 + cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */ 1726 1694 /* Error for our tunnel must arrive at LOCAL_IN */ 1727 1695 (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) { 1728 1696 __u8 iproto; ··· 1733 1699 if (unlikely(cih->frag_off & htons(IP_OFFSET))) 1734 1700 return NF_ACCEPT; 1735 1701 offset2 = offset + cih->ihl * 4; 1736 - ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr, 1737 - &iproto); 1702 + if (cih->protocol == IPPROTO_UDP) 1703 + ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, 1704 + raddr, &iproto); 1705 + else 1706 + ulen = ipvs_gre_decap(ipvs, skb, offset2, AF_INET, 1707 + raddr, &iproto); 1738 1708 if (ulen > 0) { 1739 - /* Skip IP and UDP tunnel headers */ 1709 + /* Skip IP and UDP/GRE tunnel headers */ 1740 1710 offset = offset2 + ulen; 1741 1711 /* Now we should be at the original IP header */ 1742 1712 cih = skb_header_pointer(skb, offset, sizeof(_ciph),