diff --git a/kernel-patch/kernel-5.17.0-eoip-buildconf.patch b/kernel-patch/kernel-5.17.0-eoip-buildconf.patch new file mode 120000 index 0000000..6496237 --- /dev/null +++ b/kernel-patch/kernel-5.17.0-eoip-buildconf.patch @@ -0,0 +1 @@ +kernel-5.6.0-eoip-buildconf.patch \ No newline at end of file diff --git a/kernel-patch/kernel-5.17.0-eoip-gre-demux.patch b/kernel-patch/kernel-5.17.0-eoip-gre-demux.patch new file mode 120000 index 0000000..da9cb30 --- /dev/null +++ b/kernel-patch/kernel-5.17.0-eoip-gre-demux.patch @@ -0,0 +1 @@ +kernel-5.6.0-eoip-gre-demux.patch \ No newline at end of file diff --git a/kernel-patch/kernel-5.17.0-eoip.patch b/kernel-patch/kernel-5.17.0-eoip.patch new file mode 100644 index 0000000..c636a08 --- /dev/null +++ b/kernel-patch/kernel-5.17.0-eoip.patch @@ -0,0 +1,955 @@ +--- linux-5.17.3/net/ipv4/eoip.c.orig ++++ linux-5.17.3/net/ipv4/eoip.c +@@ -0,0 +1,952 @@ ++/* ++ * Linux NET3: EOIP over IP protocol decoder. ++ * ++ * Based on the code from ip_gre.c ++ * ++ * Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru) ++ * Authors: Boian Bonev (bbonev@ipacct.com) ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++#include ++#include ++#include ++#endif ++ ++static struct rtnl_link_ops eoip_ops __read_mostly; ++static int eoip_tunnel_bind_dev(struct net_device *dev); ++static void eoip_setup(struct net_device *dev); ++ ++/* Fallback tunnel: no source, no destination, no key, no options */ ++ ++#define EOIP_HASH_SIZE 16 ++ ++static int eoip_net_id __read_mostly; ++struct eoip_net { ++ struct ip_tunnel __rcu *tunnels[EOIP_HASH_SIZE]; ++}; ++ ++/* Tunnel hash table */ ++ ++#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF) ++ ++/* ++ * Locking : hash tables are protected by RCU and RTNL ++ */ ++ ++/* often modified stats are per cpu, other are shared (netdev->stats) */ ++struct pcpu_tstats { ++ unsigned long rx_packets; ++ unsigned long rx_bytes; ++ unsigned long tx_packets; ++ unsigned long tx_bytes; ++}; ++ ++static struct net_device_stats *eoip_if_get_stats(struct net_device *dev) ++{ ++ struct pcpu_sw_netstats sum = { 0 }; ++ int i; ++ ++ for_each_possible_cpu(i) { ++ const struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, i); ++ ++ sum.rx_packets += tstats->rx_packets; ++ sum.rx_bytes += tstats->rx_bytes; ++ sum.tx_packets += tstats->tx_packets; ++ sum.tx_bytes += tstats->tx_bytes; ++ } ++ dev->stats.rx_packets = sum.rx_packets; ++ dev->stats.rx_bytes = sum.rx_bytes; ++ dev->stats.tx_packets = sum.tx_packets; ++ dev->stats.tx_bytes = sum.tx_bytes; ++ return &dev->stats; ++} ++ ++/* Given src, dst and key, find appropriate for input tunnel. */ ++ ++static struct ip_tunnel *eoip_tunnel_lookup(struct net_device *dev, ++ __be32 remote, __be32 local, ++ __be32 key) ++{ ++ struct net *net = dev_net(dev); ++ int link = dev->ifindex; ++ unsigned int h0 = HASH(key); ++ struct ip_tunnel *t, *cand = NULL; ++ struct eoip_net *ign = net_generic(net, eoip_net_id); ++ int dev_type = ARPHRD_ETHER; ++ int score, cand_score = 4; ++ ++ for_each_ip_tunnel_rcu(t, ign->tunnels[h0]) { ++ if (local != t->parms.iph.saddr || ++ remote != t->parms.iph.daddr || ++ key != t->parms.i_key || ++ !(t->dev->flags & IFF_UP)) ++ continue; ++ ++ score = 0; ++ if (t->parms.link != link) ++ score |= 1; ++ if (t->dev->type != dev_type) ++ score |= 2; ++ if (score == 0) ++ return t; ++ ++ if (score < cand_score) { ++ cand = t; ++ cand_score = score; ++ } ++ } ++ ++ if (cand != NULL) ++ return cand; ++ ++ return NULL; ++} ++ ++static struct ip_tunnel __rcu **__eoip_bucket(struct eoip_net *ign, ++ struct ip_tunnel_parm *parms) ++{ ++ __be32 key = parms->i_key; ++ unsigned int h = HASH(key); ++ ++ return &ign->tunnels[h]; ++} ++ ++static inline struct ip_tunnel __rcu **eoip_bucket(struct eoip_net *ign, ++ struct ip_tunnel *t) ++{ ++ return __eoip_bucket(ign, &t->parms); ++} ++ ++static void eoip_tunnel_link(struct eoip_net *ign, struct ip_tunnel *t) ++{ ++ struct ip_tunnel __rcu **tp = eoip_bucket(ign, t); ++ ++ rcu_assign_pointer(t->next, rtnl_dereference(*tp)); ++ rcu_assign_pointer(*tp, t); ++} ++ ++static void eoip_tunnel_unlink(struct eoip_net *ign, struct ip_tunnel *t) ++{ ++ struct ip_tunnel __rcu **tp; ++ struct ip_tunnel *iter; ++ ++ for (tp = eoip_bucket(ign, t); ++ (iter = rtnl_dereference(*tp)) != NULL; ++ tp = &iter->next) { ++ if (t == iter) { ++ rcu_assign_pointer(*tp, t->next); ++ break; ++ } ++ } ++} ++ ++static struct ip_tunnel *eoip_tunnel_find(struct net *net, ++ struct ip_tunnel_parm *parms, ++ int type) ++{ ++ __be32 remote = parms->iph.daddr; ++ __be32 local = parms->iph.saddr; ++ __be32 key = parms->i_key; ++ int link = parms->link; ++ struct ip_tunnel *t; ++ struct ip_tunnel __rcu **tp; ++ struct eoip_net *ign = net_generic(net, eoip_net_id); ++ ++ for (tp = __eoip_bucket(ign, parms); ++ (t = rtnl_dereference(*tp)) != NULL; ++ tp = &t->next) ++ if (local == t->parms.iph.saddr && ++ remote == t->parms.iph.daddr && ++ key == t->parms.i_key && ++ link == t->parms.link && ++ type == t->dev->type) ++ break; ++ ++ return t; ++} ++ ++static struct ip_tunnel *eoip_tunnel_locate(struct net *net, ++ struct ip_tunnel_parm *parms, int create) ++{ ++ struct ip_tunnel *t, *nt; ++ struct net_device *dev; ++ char name[IFNAMSIZ]; ++ struct eoip_net *ign = net_generic(net, eoip_net_id); ++ ++ t = eoip_tunnel_find(net, parms, ARPHRD_ETHER); ++ if (t || !create) ++ return t; ++ ++ if (parms->name[0]) ++ strlcpy(name, parms->name, IFNAMSIZ); ++ else ++ strcpy(name, "eoip%d"); ++ ++ dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, eoip_setup); ++ if (!dev) ++ return NULL; ++ ++ dev_net_set(dev, net); ++ ++ nt = netdev_priv(dev); ++ nt->parms = *parms; ++ dev->rtnl_link_ops = &eoip_ops; ++ ++ dev->mtu = eoip_tunnel_bind_dev(dev); ++ ++ if (register_netdevice(dev) < 0) ++ goto failed_free; ++ ++ dev_hold(dev); ++ eoip_tunnel_link(ign, nt); ++ return nt; ++ ++failed_free: ++ free_netdev(dev); ++ return NULL; ++} ++ ++static void eoip_if_uninit(struct net_device *dev) ++{ ++ struct net *net = dev_net(dev); ++ struct eoip_net *ign = net_generic(net, eoip_net_id); ++ ++ eoip_tunnel_unlink(ign, netdev_priv(dev)); ++ dev_put(dev); ++} ++ ++ ++static void eoip_err(struct sk_buff *skb, u32 info) ++{ ++ ++/* 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. ++ ++ Moreover, Cisco "wise men" put GRE key to the third word ++ in GRE header. It makes impossible maintaining even soft state for keyed ++ GRE tunnels with enabled checksum. Tell them "thank you". ++ ++ Well, I wonder, rfc1812 was written by Cisco employee, ++ what the hell these idiots break standrads established ++ by themself??? ++ */ ++ ++ const struct iphdr *iph = (const struct iphdr *)skb->data; ++ __be16 *p = (__be16 *)(skb->data+(iph->ihl<<2)); ++ int grehlen = (iph->ihl<<2) + 4; ++ const int type = icmp_hdr(skb)->type; ++ const int code = icmp_hdr(skb)->code; ++ struct ip_tunnel *t; ++ __be16 flags; ++ ++ flags = p[0]; ++ if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) { ++ if (flags&(GRE_VERSION|GRE_ROUTING)) ++ return; ++ if (flags&GRE_KEY) { ++ grehlen += 4; ++ if (flags&GRE_CSUM) ++ grehlen += 4; ++ } ++ } ++ ++ /* If only 8 bytes returned, keyed message will be dropped here */ ++ if (skb_headlen(skb) < grehlen) ++ return; ++ ++ switch (type) { ++ default: ++ case ICMP_PARAMETERPROB: ++ return; ++ ++ case ICMP_DEST_UNREACH: ++ switch (code) { ++ case ICMP_SR_FAILED: ++ case ICMP_PORT_UNREACH: ++ /* Impossible event. */ ++ return; ++ case ICMP_FRAG_NEEDED: ++ /* Soft state for pmtu is maintained by IP core. */ ++ return; ++ 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; ++ break; ++ } ++ ++ rcu_read_lock(); ++ t = eoip_tunnel_lookup(skb->dev, iph->daddr, iph->saddr, ++ flags & GRE_KEY ? ++ *(((__be32 *)p) + (grehlen / 4) - 1) : 0); ++ if (t == NULL || t->parms.iph.daddr == 0 || ++ ipv4_is_multicast(t->parms.iph.daddr)) ++ goto out; ++ ++ if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) ++ goto out; ++ ++ if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO)) ++ t->err_count++; ++ else ++ t->err_count = 1; ++ t->err_time = jiffies; ++out: ++ rcu_read_unlock(); ++} ++ ++static int eoip_rcv(struct sk_buff *skb) ++{ ++ const struct iphdr *iph; ++ u8 *h; ++ __be16 flags; ++ __be32 key = 0; ++ struct ip_tunnel *tunnel; ++ int offset = 8; ++ ++ /* ip header (20 bytes) + eoip header (8 bytes) */ ++ if (!pskb_may_pull(skb, 28)) ++ goto drop_nolock; ++ ++ iph = ip_hdr(skb); ++ h = skb->data; ++ flags = *(__be16 *)h; ++ ++ key = le16_to_cpu(*(__le16 *)(h + 6)); ++ ++ rcu_read_lock(); ++ ++ tunnel = eoip_tunnel_lookup(skb->dev, iph->saddr, iph->daddr, key); ++ if (tunnel) { ++ struct pcpu_sw_netstats *tstats; ++ ++ secpath_reset(skb); ++ ++ skb->protocol = ARPHRD_ETHER; ++ ++ skb->mac_header = skb->network_header; ++ __pskb_pull(skb, offset); ++ skb_postpull_rcsum(skb, skb_transport_header(skb), offset); ++ skb->pkt_type = PACKET_HOST; ++#ifdef CONFIG_NET_IPGRE_BROADCAST ++ if (ipv4_is_multicast(iph->daddr)) { ++ /* Looped back packet, drop it! */ ++ if (rt_is_output_route(skb_rtable(skb))) ++ goto drop; ++ tunnel->dev->stats.multicast++; ++ skb->pkt_type = PACKET_BROADCAST; ++ } ++#endif ++ ++ /* Warning: All skb pointers will be invalidated! */ ++ if (!pskb_may_pull(skb, ETH_HLEN)) { ++ tunnel->dev->stats.rx_length_errors++; ++ tunnel->dev->stats.rx_errors++; ++ goto drop; ++ } ++ ++ iph = ip_hdr(skb); ++ skb->protocol = eth_type_trans(skb, tunnel->dev); ++ skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); ++ ++ tstats = this_cpu_ptr(tunnel->dev->tstats); ++ tstats->rx_packets++; ++ tstats->rx_bytes += skb->len; ++ ++ __skb_tunnel_rx(skb, tunnel->dev, dev_net(tunnel->dev)); ++ ++ skb_reset_network_header(skb); ++ ++ netif_rx(skb); ++ ++ rcu_read_unlock(); ++ return 0; ++ } ++ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); ++ ++drop: ++ rcu_read_unlock(); ++drop_nolock: ++ kfree_skb(skb); ++ return 0; ++} ++#define __IPTUNNEL_XMIT(stats1, stats2) do { \ ++ int err; \ ++ int pkt_len = skb->len - skb_transport_offset(skb); \ ++ \ ++ skb->ip_summed = CHECKSUM_NONE; \ ++ ip_select_ident(dev_net(skb->dev), skb, NULL); \ ++ \ ++ err = ip_local_out(dev_net(skb->dev), NULL, skb); \ ++ if (likely(net_xmit_eval(err) == 0)) { \ ++ (stats1)->tx_bytes += pkt_len; \ ++ (stats1)->tx_packets++; \ ++ } else { \ ++ (stats2)->tx_errors++; \ ++ (stats2)->tx_aborted_errors++; \ ++ } \ ++} while (0) ++ ++static netdev_tx_t eoip_if_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct ip_tunnel *tunnel = netdev_priv(dev); ++ struct pcpu_sw_netstats *tstats; ++ const struct iphdr *old_iph = ip_hdr(skb); ++ const struct iphdr *tiph; ++ struct flowi4 fl4; ++ u8 tos; ++ 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; ++ uint16_t frame_size; ++ ++ IPCB(skb)->flags = 0; ++ ++ gre_hlen = tunnel->hlen; ++ tiph = &tunnel->parms.iph; ++ dst = tiph->daddr; ++ ++ tos = tiph->tos; ++ ++ frame_size = skb->len; ++ ++ rt = ip_route_output_gre(dev_net(dev), &fl4, dst, tiph->saddr, ++ tunnel->parms.o_key, RT_TOS(tos), ++ tunnel->parms.link); ++ if (IS_ERR(rt)) { ++ dev->stats.tx_carrier_errors++; ++ goto tx_error; ++ } ++ tdev = rt->dst.dev; ++ ++ if (tdev == dev) { ++ ip_rt_put(rt); ++ dev->stats.collisions++; ++ goto tx_error; ++ } ++ ++ mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu; ++ ++ if (skb_dst(skb)) ++ skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu, true); ++ ++ if (tunnel->err_count > 0) { ++ if (time_before(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 + rt->dst.header_len; ++ ++ if (skb_headroom(skb) < max_headroom || skb_shared(skb) || ++ (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { ++ struct sk_buff *new_skb; ++ ++ new_skb = skb_realloc_headroom(skb, max_headroom); ++ if (!new_skb) { ++ ip_rt_put(rt); ++ dev->stats.tx_dropped++; ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++ } ++ if (skb->sk) ++ skb_set_owner_w(new_skb, skb->sk); ++ dev_kfree_skb(skb); ++ skb = new_skb; ++ old_iph = ip_hdr(skb); ++ } ++ ++ skb_reset_transport_header(skb); ++ 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); ++ skb_dst_drop(skb); ++ skb_dst_set(skb, &rt->dst); ++ ++ /* ++ * Push down and install the EOIP header. ++ */ ++ ++ iph = ip_hdr(skb); ++ iph->version = 4; ++ iph->ihl = sizeof(struct iphdr) >> 2; ++ iph->frag_off = 0; ++ iph->protocol = IPPROTO_GRE; ++ iph->tos = tos; ++ iph->daddr = fl4.daddr; ++ iph->saddr = fl4.saddr; ++ iph->ttl = tiph->ttl; ++ ++ if (iph->ttl == 0) ++ iph->ttl = ip4_dst_hoplimit(&rt->dst); ++ ++ ((u8 *)(iph + 1))[0] = 0x20; ++ ((u8 *)(iph + 1))[1] = 0x01; ++ ((u8 *)(iph + 1))[2] = 0x64; ++ ((u8 *)(iph + 1))[3] = 0x00; ++ ++ ((__be16 *)(iph + 1))[2] = htons(frame_size); ++ ((__le16 *)(iph + 1))[3] = cpu_to_le16(tunnel->parms.i_key); ++ ++ nf_reset_ct(skb); ++ tstats = this_cpu_ptr(dev->tstats); ++ __IPTUNNEL_XMIT(tstats, &dev->stats); ++ //ip_tunnel_xmit(skb, dev, tiph, tiph->protocol); ++ return NETDEV_TX_OK; ++ ++tx_error: ++ dev->stats.tx_errors++; ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++} ++ ++static int eoip_tunnel_bind_dev(struct net_device *dev) ++{ ++ struct net_device *tdev = NULL; ++ struct ip_tunnel *tunnel; ++ const struct iphdr *iph; ++ int hlen = LL_MAX_HEADER; ++ int mtu = ETH_DATA_LEN; ++ int addend = sizeof(struct iphdr) + 8; ++ ++ tunnel = netdev_priv(dev); ++ iph = &tunnel->parms.iph; ++ ++ /* Guess output device to choose reasonable mtu and needed_headroom */ ++ ++ if (iph->daddr) { ++ struct flowi4 fl4; ++ struct rtable *rt; ++ ++ rt = ip_route_output_gre(dev_net(dev), &fl4, ++ iph->daddr, iph->saddr, ++ tunnel->parms.o_key, ++ RT_TOS(iph->tos), ++ tunnel->parms.link); ++ if (!IS_ERR(rt)) { ++ tdev = rt->dst.dev; ++ ip_rt_put(rt); ++ } ++ } ++ ++ if (!tdev && tunnel->parms.link) ++ tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link); ++ ++ if (tdev) { ++ hlen = tdev->hard_header_len + tdev->needed_headroom; ++ mtu = tdev->mtu; ++ } ++ dev->ifindex = tunnel->parms.link; ++ ++ dev->needed_headroom = addend + hlen; ++ mtu -= dev->hard_header_len + addend; ++ ++ if (mtu < 68) ++ mtu = 68; ++ ++ tunnel->hlen = addend; ++ ++ return mtu; ++} ++ ++static int eoip_if_change_mtu(struct net_device *dev, int new_mtu) ++{ ++ struct ip_tunnel *tunnel = netdev_priv(dev); ++ if (new_mtu < 68 || ++ new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen) ++ return -EINVAL; ++ dev->mtu = new_mtu; ++ return 0; ++} ++ ++static void eoip_dev_free(struct net_device *dev) ++{ ++ free_percpu(dev->tstats); ++ free_netdev(dev); ++} ++ ++static const struct gre_protocol eoip_protocol = { ++ .handler = eoip_rcv, ++ .err_handler = eoip_err, ++}; ++ ++static void eoip_destroy_tunnels(struct eoip_net *ign, struct list_head *head) ++{ ++ int h; ++ for (h = 0; h < EOIP_HASH_SIZE; h++) { ++ struct ip_tunnel *t; ++ ++ t = rtnl_dereference(ign->tunnels[h]); ++ ++ while (t != NULL) { ++ unregister_netdevice_queue(t->dev, head); ++ t = rtnl_dereference(t->next); ++ } ++ } ++} ++ ++static int __net_init eoip_init_net(struct net *net) ++{ ++ return 0; ++} ++ ++static void __net_exit eoip_exit_net(struct net *net) ++{ ++ struct eoip_net *ign; ++ LIST_HEAD(list); ++ ++ ign = net_generic(net, eoip_net_id); ++ rtnl_lock(); ++ eoip_destroy_tunnels(ign, &list); ++ unregister_netdevice_many(&list); ++ rtnl_unlock(); ++} ++ ++static struct pernet_operations eoip_net_ops = { ++ .id = &eoip_net_id, ++ .init = eoip_init_net, ++ .exit = eoip_exit_net, ++ .size = sizeof(struct eoip_net), ++}; ++ ++static int eoip_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) ++{ ++ return 0; ++} ++ ++static int eoip_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) ++{ ++ __be32 daddr; ++ ++ if (tb[IFLA_ADDRESS]) { ++ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) ++ return -EINVAL; ++ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) ++ return -EADDRNOTAVAIL; ++ } ++ ++ if (!data) ++ goto out; ++ ++ if (data[IFLA_GRE_REMOTE]) { ++ memcpy(&daddr, nla_data(data[IFLA_GRE_REMOTE]), 4); ++ if (!daddr) ++ return -EINVAL; ++ } ++ ++out: ++ return eoip_tunnel_validate(tb, data); ++} ++ ++static void eoip_netlink_parms(struct nlattr *data[], ++ struct ip_tunnel_parm *parms) ++{ ++ memset(parms, 0, sizeof(*parms)); ++ ++ parms->iph.protocol = IPPROTO_GRE; ++ ++ if (!data) ++ return; ++ ++ if (data[IFLA_GRE_LINK]) ++ parms->link = nla_get_u32(data[IFLA_GRE_LINK]); ++ ++ if (data[IFLA_GRE_IKEY]) ++ parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]); ++ ++ if (data[IFLA_GRE_LOCAL]) ++ parms->iph.saddr = nla_get_be32(data[IFLA_GRE_LOCAL]); ++ ++ if (data[IFLA_GRE_REMOTE]) ++ parms->iph.daddr = nla_get_be32(data[IFLA_GRE_REMOTE]); ++ ++ if (data[IFLA_GRE_TTL]) ++ parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]); ++ ++ if (data[IFLA_GRE_TOS]) ++ parms->iph.tos = nla_get_u8(data[IFLA_GRE_TOS]); ++} ++ ++static int eoip_if_init(struct net_device *dev) ++{ ++ struct ip_tunnel *tunnel; ++ ++ tunnel = netdev_priv(dev); ++ ++ tunnel->dev = dev; ++ strcpy(tunnel->parms.name, dev->name); ++ ++ eoip_tunnel_bind_dev(dev); ++ ++ dev->tstats = alloc_percpu(struct pcpu_sw_netstats); ++ if (!dev->tstats) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static const struct net_device_ops eoip_netdev_ops = { ++ .ndo_init = eoip_if_init, ++ .ndo_uninit = eoip_if_uninit, ++ .ndo_start_xmit = eoip_if_xmit, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_change_mtu = eoip_if_change_mtu, ++ .ndo_get_stats = eoip_if_get_stats, ++}; ++ ++static void eoip_setup(struct net_device *dev) ++{ ++ ether_setup(dev); ++ ++ dev->netdev_ops = &eoip_netdev_ops; ++ dev->priv_destructor = eoip_dev_free; ++ ++ dev->ifindex = 0; ++ dev->features |= NETIF_F_NETNS_LOCAL; ++} ++ ++static int eoip_newlink(struct net *src_net, struct net_device *dev, ++ struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) ++{ ++ struct ip_tunnel *nt; ++ struct net *net = dev_net(dev); ++ struct eoip_net *ign = net_generic(net, eoip_net_id); ++ int mtu; ++ int err; ++ ++ nt = netdev_priv(dev); ++ eoip_netlink_parms(data, &nt->parms); ++ ++ if (eoip_tunnel_find(net, &nt->parms, dev->type)) ++ return -EEXIST; ++ ++ if (!tb[IFLA_ADDRESS]) { ++ uint8_t addr[ETH_ALEN]; ++ ++ eth_random_addr(addr); ++ dev_addr_set(dev,addr); ++ } ++ ++ mtu = eoip_tunnel_bind_dev(dev); ++ ++ /* Can use a lockless transmit, we do not generate output sequences */ ++ dev->features |= NETIF_F_LLTX; ++ ++ err = register_netdevice(dev); ++ if (err) ++ goto out; ++ ++ dev_hold(dev); ++ eoip_tunnel_link(ign, nt); ++ ++out: ++ return err; ++} ++ ++static int eoip_changelink(struct net_device *dev, struct nlattr *tb[], ++ struct nlattr *data[], struct netlink_ext_ack *extack) ++{ ++ struct ip_tunnel *t, *nt; ++ struct net *net = dev_net(dev); ++ struct eoip_net *ign = net_generic(net, eoip_net_id); ++ struct ip_tunnel_parm p; ++ int mtu; ++ ++ nt = netdev_priv(dev); ++ eoip_netlink_parms(data, &p); ++ ++ t = eoip_tunnel_locate(net, &p, 0); ++ ++ if (t) { ++ if (t->dev != dev) ++ return -EEXIST; ++ } else { ++ t = nt; ++ ++ eoip_tunnel_unlink(ign, t); ++ t->parms.iph.saddr = p.iph.saddr; ++ t->parms.iph.daddr = p.iph.daddr; ++ t->parms.i_key = p.i_key; ++ eoip_tunnel_link(ign, t); ++ netdev_state_change(dev); ++ } ++ ++ t->parms.iph.ttl = p.iph.ttl; ++ t->parms.iph.tos = p.iph.tos; ++ t->parms.iph.frag_off = p.iph.frag_off; ++ ++ if (t->parms.link != p.link) { ++ t->parms.link = p.link; ++ mtu = eoip_tunnel_bind_dev(dev); ++ netdev_state_change(dev); ++ } ++ ++ return 0; ++} ++ ++static size_t eoip_get_size(const struct net_device *dev) ++{ ++ return ++ /* IFLA_GRE_LINK */ ++ nla_total_size(4) + ++ /* IFLA_GRE_IKEY */ ++ nla_total_size(4) + ++ /* IFLA_GRE_LOCAL */ ++ nla_total_size(4) + ++ /* IFLA_GRE_REMOTE */ ++ nla_total_size(4) + ++ /* IFLA_GRE_TTL */ ++ nla_total_size(1) + ++ /* IFLA_GRE_TOS */ ++ nla_total_size(1) + ++ 0; ++} ++ ++static int eoip_fill_info(struct sk_buff *skb, const struct net_device *dev) ++{ ++ struct ip_tunnel *t = netdev_priv(dev); ++ struct ip_tunnel_parm *p = &t->parms; ++ ++ if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) || ++ nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || ++ nla_put_be32(skb, IFLA_GRE_LOCAL, p->iph.saddr) || ++ nla_put_be32(skb, IFLA_GRE_REMOTE, p->iph.daddr) || ++ nla_put_u8(skb, IFLA_GRE_TTL, p->iph.ttl) || ++ nla_put_u8(skb, IFLA_GRE_TOS, p->iph.tos)) ++ goto nla_put_failure; ++ ++ return 0; ++ ++nla_put_failure: ++ return -EMSGSIZE; ++} ++ ++static const struct nla_policy eoip_policy[IFLA_GRE_MAX + 1] = { ++ [IFLA_GRE_LINK] = { .type = NLA_U32 }, ++ [IFLA_GRE_IKEY] = { .type = NLA_U32 }, ++ [IFLA_GRE_LOCAL] = { .len = sizeof_field(struct iphdr, saddr) }, ++ [IFLA_GRE_REMOTE] = { .len = sizeof_field(struct iphdr, daddr) }, ++ [IFLA_GRE_TTL] = { .type = NLA_U8 }, ++ [IFLA_GRE_TOS] = { .type = NLA_U8 }, ++}; ++ ++static struct rtnl_link_ops eoip_ops __read_mostly = { ++ .kind = "eoip", ++ .maxtype = IFLA_GRE_MAX, ++ .policy = eoip_policy, ++ .priv_size = sizeof(struct ip_tunnel), ++ .setup = eoip_setup, ++ .validate = eoip_validate, ++ .newlink = eoip_newlink, ++ .changelink = eoip_changelink, ++ .get_size = eoip_get_size, ++ .fill_info = eoip_fill_info, ++}; ++ ++/* ++ * And now the modules code and kernel interface. ++ */ ++ ++static int __init eoip_init(void) ++{ ++ int err; ++ ++ printk(KERN_INFO "EoIP (IPv4) tunneling driver\n"); ++ ++ err = register_pernet_device(&eoip_net_ops); ++ if (err < 0) ++ return err; ++ ++ err = gre_add_protocol(&eoip_protocol, GREPROTO_NONSTD_EOIP); ++ if (err < 0) { ++ printk(KERN_INFO "eoip init: can't add protocol\n"); ++ goto add_proto_failed; ++ } ++ ++ err = rtnl_link_register(&eoip_ops); ++ if (err < 0) ++ goto eoip_ops_failed; ++ ++out: ++ return err; ++ ++eoip_ops_failed: ++ gre_del_protocol(&eoip_protocol, GREPROTO_NONSTD_EOIP); ++add_proto_failed: ++ unregister_pernet_device(&eoip_net_ops); ++ goto out; ++} ++ ++static void __exit eoip_fini(void) ++{ ++ rtnl_link_unregister(&eoip_ops); ++ if (gre_del_protocol(&eoip_protocol, GREPROTO_NONSTD_EOIP) < 0) ++ printk(KERN_INFO "eoip close: can't remove protocol\n"); ++ unregister_pernet_device(&eoip_net_ops); ++} ++ ++module_init(eoip_init); ++module_exit(eoip_fini); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_RTNL_LINK("eoip"); diff --git a/out-of-tree-5.17.x/Makefile b/out-of-tree-5.17.x/Makefile new file mode 100644 index 0000000..2e2d9bf --- /dev/null +++ b/out-of-tree-5.17.x/Makefile @@ -0,0 +1,24 @@ +ifneq ($(KERNELRELEASE),) +# ugly and dirty hack to avoid patching system's kernel headers +ccflags-y += "-DGREPROTO_NONSTD_EOIP=0x80" +ccflags-y += "-DGREPROTO_NONSTD_MAX=0x81" +ccflags-y += "-DGREPROTO_NONSTD_BASE=0x80" +obj-m := eoip.o +obj-m += gre.o +else +KDIR ?= /lib/modules/`uname -r`/build +default: eoip.c gre.c gre.h + @$(MAKE) -C $(KDIR) M=$$PWD +eoip.c: ../kernel-patch/kernel-5.17.0-eoip.patch + @patch -p3 < $< +gre.c gre.h: ../kernel-patch/kernel-5.17.0-eoip-gre-demux.patch + @if [ -f /lib/modules/`uname -r`/build/net/ipv4/gre.c ]; then echo found running kernel version of gre.c; cp /lib/modules/`uname -r`/build/net/ipv4/gre_demux.c gre_demux.c; else echo using 5.17.0 version of gre_demux.c; cp gre_demux-5.17.0.c gre_demux.c; fi + @if [ -f /lib/modules/`uname -r`/build/include/net/gre.h ]; then echo found running kernel version of gre.h; cp /lib/modules/`uname -r`/build/include/net/gre.h gre.h; else echo using 5.17.0 version of gre.h; cp gre-5.17.0.h gre.h; fi + @patch -p3 < $< + @cp gre_demux.c gre.c +clean: + @rm -f gre_demux.c gre.c gre.h eoip.c *.orig *.rej + @$(MAKE) -C $(KDIR) M=$$PWD clean +install modules_install: + @$(MAKE) -C $(KDIR) M=$$PWD modules_install +endif diff --git a/out-of-tree-5.17.x/gre-5.17.0.h b/out-of-tree-5.17.x/gre-5.17.0.h new file mode 100644 index 0000000..4e20970 --- /dev/null +++ b/out-of-tree-5.17.x/gre-5.17.0.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_GRE_H +#define __LINUX_GRE_H + +#include +#include + +struct gre_base_hdr { + __be16 flags; + __be16 protocol; +} __packed; + +struct gre_full_hdr { + struct gre_base_hdr fixed_header; + __be16 csum; + __be16 reserved1; + __be32 key; + __be32 seq; +} __packed; +#define GRE_HEADER_SECTION 4 + +#define GREPROTO_CISCO 0 +#define GREPROTO_PPTP 1 +#define GREPROTO_MAX 2 +#define GRE_IP_PROTO_MAX 2 + +struct gre_protocol { + int (*handler)(struct sk_buff *skb); + void (*err_handler)(struct sk_buff *skb, u32 info); +}; + +int gre_add_protocol(const struct gre_protocol *proto, u8 version); +int gre_del_protocol(const struct gre_protocol *proto, u8 version); + +struct net_device *gretap_fb_dev_create(struct net *net, const char *name, + u8 name_assign_type); +int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, + bool *csum_err, __be16 proto, int nhs); + +static inline bool netif_is_gretap(const struct net_device *dev) +{ + return dev->rtnl_link_ops && + !strcmp(dev->rtnl_link_ops->kind, "gretap"); +} + +static inline bool netif_is_ip6gretap(const struct net_device *dev) +{ + return dev->rtnl_link_ops && + !strcmp(dev->rtnl_link_ops->kind, "ip6gretap"); +} + +static inline int gre_calc_hlen(__be16 o_flags) +{ + int addend = 4; + + if (o_flags & TUNNEL_CSUM) + addend += 4; + if (o_flags & TUNNEL_KEY) + addend += 4; + if (o_flags & TUNNEL_SEQ) + addend += 4; + return addend; +} + +static inline __be16 gre_flags_to_tnl_flags(__be16 flags) +{ + __be16 tflags = 0; + + if (flags & GRE_CSUM) + tflags |= TUNNEL_CSUM; + if (flags & GRE_ROUTING) + tflags |= TUNNEL_ROUTING; + if (flags & GRE_KEY) + tflags |= TUNNEL_KEY; + if (flags & GRE_SEQ) + tflags |= TUNNEL_SEQ; + if (flags & GRE_STRICT) + tflags |= TUNNEL_STRICT; + if (flags & GRE_REC) + tflags |= TUNNEL_REC; + if (flags & GRE_VERSION) + tflags |= TUNNEL_VERSION; + + return tflags; +} + +static inline __be16 gre_tnl_flags_to_gre_flags(__be16 tflags) +{ + __be16 flags = 0; + + if (tflags & TUNNEL_CSUM) + flags |= GRE_CSUM; + if (tflags & TUNNEL_ROUTING) + flags |= GRE_ROUTING; + if (tflags & TUNNEL_KEY) + flags |= GRE_KEY; + if (tflags & TUNNEL_SEQ) + flags |= GRE_SEQ; + if (tflags & TUNNEL_STRICT) + flags |= GRE_STRICT; + if (tflags & TUNNEL_REC) + flags |= GRE_REC; + if (tflags & TUNNEL_VERSION) + flags |= GRE_VERSION; + + return flags; +} + +static inline void gre_build_header(struct sk_buff *skb, int hdr_len, + __be16 flags, __be16 proto, + __be32 key, __be32 seq) +{ + struct gre_base_hdr *greh; + + skb_push(skb, hdr_len); + + skb_set_inner_protocol(skb, proto); + skb_reset_transport_header(skb); + greh = (struct gre_base_hdr *)skb->data; + greh->flags = gre_tnl_flags_to_gre_flags(flags); + greh->protocol = proto; + + if (flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) { + __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); + + if (flags & TUNNEL_SEQ) { + *ptr = seq; + ptr--; + } + if (flags & TUNNEL_KEY) { + *ptr = key; + ptr--; + } + if (flags & TUNNEL_CSUM && + !(skb_shinfo(skb)->gso_type & + (SKB_GSO_GRE | SKB_GSO_GRE_CSUM))) { + *ptr = 0; + if (skb->ip_summed == CHECKSUM_PARTIAL) { + *(__sum16 *)ptr = csum_fold(lco_csum(skb)); + } else { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = sizeof(*greh); + } + } + } +} + +#endif diff --git a/out-of-tree-5.17.x/gre_demux-5.17.0.c b/out-of-tree-5.17.x/gre_demux-5.17.0.c new file mode 100644 index 0000000..cbb2b4b --- /dev/null +++ b/out-of-tree-5.17.x/gre_demux-5.17.0.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GRE over IPv4 demultiplexer driver + * + * Authors: Dmitry Kozlov (xeb@mail.ru) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; + +int gre_add_protocol(const struct gre_protocol *proto, u8 version) +{ + if (version >= GREPROTO_MAX) + return -EINVAL; + + return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ? + 0 : -EBUSY; +} +EXPORT_SYMBOL_GPL(gre_add_protocol); + +int gre_del_protocol(const struct gre_protocol *proto, u8 version) +{ + int ret; + + if (version >= GREPROTO_MAX) + return -EINVAL; + + ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ? + 0 : -EBUSY; + + if (ret) + return ret; + + synchronize_rcu(); + return 0; +} +EXPORT_SYMBOL_GPL(gre_del_protocol); + +/* Fills in tpi and returns header length to be pulled. + * Note that caller must use pskb_may_pull() before pulling GRE header. + */ +int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, + bool *csum_err, __be16 proto, int nhs) +{ + const struct gre_base_hdr *greh; + __be32 *options; + int hdr_len; + + if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr)))) + return -EINVAL; + + greh = (struct gre_base_hdr *)(skb->data + nhs); + if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING))) + return -EINVAL; + + tpi->flags = gre_flags_to_tnl_flags(greh->flags); + hdr_len = gre_calc_hlen(tpi->flags); + + if (!pskb_may_pull(skb, nhs + hdr_len)) + return -EINVAL; + + greh = (struct gre_base_hdr *)(skb->data + nhs); + tpi->proto = greh->protocol; + + options = (__be32 *)(greh + 1); + if (greh->flags & GRE_CSUM) { + if (!skb_checksum_simple_validate(skb)) { + skb_checksum_try_convert(skb, IPPROTO_GRE, + null_compute_pseudo); + } else if (csum_err) { + *csum_err = true; + return -EINVAL; + } + + options++; + } + + if (greh->flags & GRE_KEY) { + tpi->key = *options; + options++; + } else { + tpi->key = 0; + } + if (unlikely(greh->flags & GRE_SEQ)) { + tpi->seq = *options; + options++; + } else { + tpi->seq = 0; + } + /* WCCP version 1 and 2 protocol decoding. + * - Change protocol to IPv4/IPv6 + * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header + */ + if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) { + u8 _val, *val; + + val = skb_header_pointer(skb, nhs + hdr_len, + sizeof(_val), &_val); + if (!val) + return -EINVAL; + tpi->proto = proto; + if ((*val & 0xF0) != 0x40) + hdr_len += 4; + } + tpi->hdr_len = hdr_len; + + /* ERSPAN ver 1 and 2 protocol sets GRE key field + * to 0 and sets the configured key in the + * inner erspan header field + */ + if ((greh->protocol == htons(ETH_P_ERSPAN) && hdr_len != 4) || + greh->protocol == htons(ETH_P_ERSPAN2)) { + struct erspan_base_hdr *ershdr; + + if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr))) + return -EINVAL; + + ershdr = (struct erspan_base_hdr *)(skb->data + nhs + hdr_len); + tpi->key = cpu_to_be32(get_session_id(ershdr)); + } + + return hdr_len; +} +EXPORT_SYMBOL(gre_parse_header); + +static int gre_rcv(struct sk_buff *skb) +{ + const struct gre_protocol *proto; + u8 ver; + int ret; + + if (!pskb_may_pull(skb, 12)) + goto drop; + + ver = skb->data[1]&0x7f; + if (ver >= GREPROTO_MAX) + goto drop; + + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (!proto || !proto->handler) + goto drop_unlock; + ret = proto->handler(skb); + rcu_read_unlock(); + return ret; + +drop_unlock: + rcu_read_unlock(); +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static int gre_err(struct sk_buff *skb, u32 info) +{ + const struct gre_protocol *proto; + const struct iphdr *iph = (const struct iphdr *)skb->data; + u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f; + int err = 0; + + if (ver >= GREPROTO_MAX) + return -EINVAL; + + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (proto && proto->err_handler) + proto->err_handler(skb, info); + else + err = -EPROTONOSUPPORT; + rcu_read_unlock(); + + return err; +} + +static const struct net_protocol net_gre_protocol = { + .handler = gre_rcv, + .err_handler = gre_err, +}; + +static int __init gre_init(void) +{ + pr_info("GRE over IPv4 demultiplexor driver\n"); + + if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { + pr_err("can't add protocol\n"); + return -EAGAIN; + } + return 0; +} + +static void __exit gre_exit(void) +{ + inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); +} + +module_init(gre_init); +module_exit(gre_exit); + +MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver"); +MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); +MODULE_LICENSE("GPL");