162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Bareudp: UDP tunnel encasulation for different Payload types like 362306a36Sopenharmony_ci * MPLS, NSH, IP, etc. 462306a36Sopenharmony_ci * Copyright (c) 2019 Nokia, Inc. 562306a36Sopenharmony_ci * Authors: Martin Varghese, <martin.varghese@nokia.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/hash.h> 1462306a36Sopenharmony_ci#include <net/dst_metadata.h> 1562306a36Sopenharmony_ci#include <net/gro_cells.h> 1662306a36Sopenharmony_ci#include <net/rtnetlink.h> 1762306a36Sopenharmony_ci#include <net/protocol.h> 1862306a36Sopenharmony_ci#include <net/ip6_tunnel.h> 1962306a36Sopenharmony_ci#include <net/ip_tunnels.h> 2062306a36Sopenharmony_ci#include <net/udp_tunnel.h> 2162306a36Sopenharmony_ci#include <net/bareudp.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define BAREUDP_BASE_HLEN sizeof(struct udphdr) 2462306a36Sopenharmony_ci#define BAREUDP_IPV4_HLEN (sizeof(struct iphdr) + \ 2562306a36Sopenharmony_ci sizeof(struct udphdr)) 2662306a36Sopenharmony_ci#define BAREUDP_IPV6_HLEN (sizeof(struct ipv6hdr) + \ 2762306a36Sopenharmony_ci sizeof(struct udphdr)) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic bool log_ecn_error = true; 3062306a36Sopenharmony_cimodule_param(log_ecn_error, bool, 0644); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* per-network namespace private data for this module */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic unsigned int bareudp_net_id; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct bareudp_net { 3862306a36Sopenharmony_ci struct list_head bareudp_list; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct bareudp_conf { 4262306a36Sopenharmony_ci __be16 ethertype; 4362306a36Sopenharmony_ci __be16 port; 4462306a36Sopenharmony_ci u16 sport_min; 4562306a36Sopenharmony_ci bool multi_proto_mode; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Pseudo network device */ 4962306a36Sopenharmony_cistruct bareudp_dev { 5062306a36Sopenharmony_ci struct net *net; /* netns for packet i/o */ 5162306a36Sopenharmony_ci struct net_device *dev; /* netdev for bareudp tunnel */ 5262306a36Sopenharmony_ci __be16 ethertype; 5362306a36Sopenharmony_ci __be16 port; 5462306a36Sopenharmony_ci u16 sport_min; 5562306a36Sopenharmony_ci bool multi_proto_mode; 5662306a36Sopenharmony_ci struct socket __rcu *sock; 5762306a36Sopenharmony_ci struct list_head next; /* bareudp node on namespace list */ 5862306a36Sopenharmony_ci struct gro_cells gro_cells; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct metadata_dst *tun_dst = NULL; 6462306a36Sopenharmony_ci struct bareudp_dev *bareudp; 6562306a36Sopenharmony_ci unsigned short family; 6662306a36Sopenharmony_ci unsigned int len; 6762306a36Sopenharmony_ci __be16 proto; 6862306a36Sopenharmony_ci void *oiph; 6962306a36Sopenharmony_ci int err; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci bareudp = rcu_dereference_sk_user_data(sk); 7262306a36Sopenharmony_ci if (!bareudp) 7362306a36Sopenharmony_ci goto drop; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 7662306a36Sopenharmony_ci family = AF_INET; 7762306a36Sopenharmony_ci else 7862306a36Sopenharmony_ci family = AF_INET6; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (bareudp->ethertype == htons(ETH_P_IP)) { 8162306a36Sopenharmony_ci __u8 ipversion; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (skb_copy_bits(skb, BAREUDP_BASE_HLEN, &ipversion, 8462306a36Sopenharmony_ci sizeof(ipversion))) { 8562306a36Sopenharmony_ci bareudp->dev->stats.rx_dropped++; 8662306a36Sopenharmony_ci goto drop; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci ipversion >>= 4; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (ipversion == 4) { 9162306a36Sopenharmony_ci proto = htons(ETH_P_IP); 9262306a36Sopenharmony_ci } else if (ipversion == 6 && bareudp->multi_proto_mode) { 9362306a36Sopenharmony_ci proto = htons(ETH_P_IPV6); 9462306a36Sopenharmony_ci } else { 9562306a36Sopenharmony_ci bareudp->dev->stats.rx_dropped++; 9662306a36Sopenharmony_ci goto drop; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } else if (bareudp->ethertype == htons(ETH_P_MPLS_UC)) { 9962306a36Sopenharmony_ci struct iphdr *tunnel_hdr; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci tunnel_hdr = (struct iphdr *)skb_network_header(skb); 10262306a36Sopenharmony_ci if (tunnel_hdr->version == 4) { 10362306a36Sopenharmony_ci if (!ipv4_is_multicast(tunnel_hdr->daddr)) { 10462306a36Sopenharmony_ci proto = bareudp->ethertype; 10562306a36Sopenharmony_ci } else if (bareudp->multi_proto_mode && 10662306a36Sopenharmony_ci ipv4_is_multicast(tunnel_hdr->daddr)) { 10762306a36Sopenharmony_ci proto = htons(ETH_P_MPLS_MC); 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci bareudp->dev->stats.rx_dropped++; 11062306a36Sopenharmony_ci goto drop; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci int addr_type; 11462306a36Sopenharmony_ci struct ipv6hdr *tunnel_hdr_v6; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci tunnel_hdr_v6 = (struct ipv6hdr *)skb_network_header(skb); 11762306a36Sopenharmony_ci addr_type = 11862306a36Sopenharmony_ci ipv6_addr_type((struct in6_addr *)&tunnel_hdr_v6->daddr); 11962306a36Sopenharmony_ci if (!(addr_type & IPV6_ADDR_MULTICAST)) { 12062306a36Sopenharmony_ci proto = bareudp->ethertype; 12162306a36Sopenharmony_ci } else if (bareudp->multi_proto_mode && 12262306a36Sopenharmony_ci (addr_type & IPV6_ADDR_MULTICAST)) { 12362306a36Sopenharmony_ci proto = htons(ETH_P_MPLS_MC); 12462306a36Sopenharmony_ci } else { 12562306a36Sopenharmony_ci bareudp->dev->stats.rx_dropped++; 12662306a36Sopenharmony_ci goto drop; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci proto = bareudp->ethertype; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (iptunnel_pull_header(skb, BAREUDP_BASE_HLEN, 13462306a36Sopenharmony_ci proto, 13562306a36Sopenharmony_ci !net_eq(bareudp->net, 13662306a36Sopenharmony_ci dev_net(bareudp->dev)))) { 13762306a36Sopenharmony_ci bareudp->dev->stats.rx_dropped++; 13862306a36Sopenharmony_ci goto drop; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci tun_dst = udp_tun_rx_dst(skb, family, TUNNEL_KEY, 0, 0); 14162306a36Sopenharmony_ci if (!tun_dst) { 14262306a36Sopenharmony_ci bareudp->dev->stats.rx_dropped++; 14362306a36Sopenharmony_ci goto drop; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci skb_dst_set(skb, &tun_dst->dst); 14662306a36Sopenharmony_ci skb->dev = bareudp->dev; 14762306a36Sopenharmony_ci oiph = skb_network_header(skb); 14862306a36Sopenharmony_ci skb_reset_network_header(skb); 14962306a36Sopenharmony_ci skb_reset_mac_header(skb); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (!ipv6_mod_enabled() || family == AF_INET) 15262306a36Sopenharmony_ci err = IP_ECN_decapsulate(oiph, skb); 15362306a36Sopenharmony_ci else 15462306a36Sopenharmony_ci err = IP6_ECN_decapsulate(oiph, skb); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (unlikely(err)) { 15762306a36Sopenharmony_ci if (log_ecn_error) { 15862306a36Sopenharmony_ci if (!ipv6_mod_enabled() || family == AF_INET) 15962306a36Sopenharmony_ci net_info_ratelimited("non-ECT from %pI4 " 16062306a36Sopenharmony_ci "with TOS=%#x\n", 16162306a36Sopenharmony_ci &((struct iphdr *)oiph)->saddr, 16262306a36Sopenharmony_ci ((struct iphdr *)oiph)->tos); 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci net_info_ratelimited("non-ECT from %pI6\n", 16562306a36Sopenharmony_ci &((struct ipv6hdr *)oiph)->saddr); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci if (err > 1) { 16862306a36Sopenharmony_ci ++bareudp->dev->stats.rx_frame_errors; 16962306a36Sopenharmony_ci ++bareudp->dev->stats.rx_errors; 17062306a36Sopenharmony_ci goto drop; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci len = skb->len; 17562306a36Sopenharmony_ci err = gro_cells_receive(&bareudp->gro_cells, skb); 17662306a36Sopenharmony_ci if (likely(err == NET_RX_SUCCESS)) 17762306a36Sopenharmony_ci dev_sw_netstats_rx_add(bareudp->dev, len); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_cidrop: 18162306a36Sopenharmony_ci /* Consume bad packet */ 18262306a36Sopenharmony_ci kfree_skb(skb); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int bareudp_err_lookup(struct sock *sk, struct sk_buff *skb) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int bareudp_init(struct net_device *dev) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 19562306a36Sopenharmony_ci int err; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 19862306a36Sopenharmony_ci if (!dev->tstats) 19962306a36Sopenharmony_ci return -ENOMEM; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci err = gro_cells_init(&bareudp->gro_cells, dev); 20262306a36Sopenharmony_ci if (err) { 20362306a36Sopenharmony_ci free_percpu(dev->tstats); 20462306a36Sopenharmony_ci return err; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void bareudp_uninit(struct net_device *dev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci gro_cells_destroy(&bareudp->gro_cells); 21462306a36Sopenharmony_ci free_percpu(dev->tstats); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic struct socket *bareudp_create_sock(struct net *net, __be16 port) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct udp_port_cfg udp_conf; 22062306a36Sopenharmony_ci struct socket *sock; 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci memset(&udp_conf, 0, sizeof(udp_conf)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (ipv6_mod_enabled()) 22662306a36Sopenharmony_ci udp_conf.family = AF_INET6; 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci udp_conf.family = AF_INET; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci udp_conf.local_udp_port = port; 23162306a36Sopenharmony_ci /* Open UDP socket */ 23262306a36Sopenharmony_ci err = udp_sock_create(net, &udp_conf, &sock); 23362306a36Sopenharmony_ci if (err < 0) 23462306a36Sopenharmony_ci return ERR_PTR(err); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci udp_allow_gso(sock->sk); 23762306a36Sopenharmony_ci return sock; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* Create new listen socket if needed */ 24162306a36Sopenharmony_cistatic int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct udp_tunnel_sock_cfg tunnel_cfg; 24462306a36Sopenharmony_ci struct socket *sock; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci sock = bareudp_create_sock(bareudp->net, port); 24762306a36Sopenharmony_ci if (IS_ERR(sock)) 24862306a36Sopenharmony_ci return PTR_ERR(sock); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Mark socket as an encapsulation socket */ 25162306a36Sopenharmony_ci memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); 25262306a36Sopenharmony_ci tunnel_cfg.sk_user_data = bareudp; 25362306a36Sopenharmony_ci tunnel_cfg.encap_type = 1; 25462306a36Sopenharmony_ci tunnel_cfg.encap_rcv = bareudp_udp_encap_recv; 25562306a36Sopenharmony_ci tunnel_cfg.encap_err_lookup = bareudp_err_lookup; 25662306a36Sopenharmony_ci tunnel_cfg.encap_destroy = NULL; 25762306a36Sopenharmony_ci setup_udp_tunnel_sock(bareudp->net, sock, &tunnel_cfg); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci rcu_assign_pointer(bareudp->sock, sock); 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int bareudp_open(struct net_device *dev) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 26662306a36Sopenharmony_ci int ret = 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ret = bareudp_socket_create(bareudp, bareudp->port); 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void bareudp_sock_release(struct bareudp_dev *bareudp) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct socket *sock; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci sock = bareudp->sock; 27762306a36Sopenharmony_ci rcu_assign_pointer(bareudp->sock, NULL); 27862306a36Sopenharmony_ci synchronize_net(); 27962306a36Sopenharmony_ci udp_tunnel_sock_release(sock); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int bareudp_stop(struct net_device *dev) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci bareudp_sock_release(bareudp); 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev, 29162306a36Sopenharmony_ci struct bareudp_dev *bareudp, 29262306a36Sopenharmony_ci const struct ip_tunnel_info *info) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev)); 29562306a36Sopenharmony_ci bool use_cache = ip_tunnel_dst_cache_usable(skb, info); 29662306a36Sopenharmony_ci struct socket *sock = rcu_dereference(bareudp->sock); 29762306a36Sopenharmony_ci bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); 29862306a36Sopenharmony_ci const struct ip_tunnel_key *key = &info->key; 29962306a36Sopenharmony_ci struct rtable *rt; 30062306a36Sopenharmony_ci __be16 sport, df; 30162306a36Sopenharmony_ci int min_headroom; 30262306a36Sopenharmony_ci __u8 tos, ttl; 30362306a36Sopenharmony_ci __be32 saddr; 30462306a36Sopenharmony_ci int err; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!sock) 30762306a36Sopenharmony_ci return -ESHUTDOWN; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci rt = ip_route_output_tunnel(skb, dev, bareudp->net, &saddr, info, 31062306a36Sopenharmony_ci IPPROTO_UDP, use_cache); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (IS_ERR(rt)) 31362306a36Sopenharmony_ci return PTR_ERR(rt); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci skb_tunnel_check_pmtu(skb, &rt->dst, 31662306a36Sopenharmony_ci BAREUDP_IPV4_HLEN + info->options_len, false); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci sport = udp_flow_src_port(bareudp->net, skb, 31962306a36Sopenharmony_ci bareudp->sport_min, USHRT_MAX, 32062306a36Sopenharmony_ci true); 32162306a36Sopenharmony_ci tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); 32262306a36Sopenharmony_ci ttl = key->ttl; 32362306a36Sopenharmony_ci df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; 32462306a36Sopenharmony_ci skb_scrub_packet(skb, xnet); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci err = -ENOSPC; 32762306a36Sopenharmony_ci if (!skb_pull(skb, skb_network_offset(skb))) 32862306a36Sopenharmony_ci goto free_dst; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + 33162306a36Sopenharmony_ci BAREUDP_BASE_HLEN + info->options_len + sizeof(struct iphdr); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci err = skb_cow_head(skb, min_headroom); 33462306a36Sopenharmony_ci if (unlikely(err)) 33562306a36Sopenharmony_ci goto free_dst; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci err = udp_tunnel_handle_offloads(skb, udp_sum); 33862306a36Sopenharmony_ci if (err) 33962306a36Sopenharmony_ci goto free_dst; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci skb_set_inner_protocol(skb, bareudp->ethertype); 34262306a36Sopenharmony_ci udp_tunnel_xmit_skb(rt, sock->sk, skb, saddr, info->key.u.ipv4.dst, 34362306a36Sopenharmony_ci tos, ttl, df, sport, bareudp->port, 34462306a36Sopenharmony_ci !net_eq(bareudp->net, dev_net(bareudp->dev)), 34562306a36Sopenharmony_ci !(info->key.tun_flags & TUNNEL_CSUM)); 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cifree_dst: 34962306a36Sopenharmony_ci dst_release(&rt->dst); 35062306a36Sopenharmony_ci return err; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev, 35462306a36Sopenharmony_ci struct bareudp_dev *bareudp, 35562306a36Sopenharmony_ci const struct ip_tunnel_info *info) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev)); 35862306a36Sopenharmony_ci bool use_cache = ip_tunnel_dst_cache_usable(skb, info); 35962306a36Sopenharmony_ci struct socket *sock = rcu_dereference(bareudp->sock); 36062306a36Sopenharmony_ci bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); 36162306a36Sopenharmony_ci const struct ip_tunnel_key *key = &info->key; 36262306a36Sopenharmony_ci struct dst_entry *dst = NULL; 36362306a36Sopenharmony_ci struct in6_addr saddr, daddr; 36462306a36Sopenharmony_ci int min_headroom; 36562306a36Sopenharmony_ci __u8 prio, ttl; 36662306a36Sopenharmony_ci __be16 sport; 36762306a36Sopenharmony_ci int err; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!sock) 37062306a36Sopenharmony_ci return -ESHUTDOWN; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci dst = ip6_dst_lookup_tunnel(skb, dev, bareudp->net, sock, &saddr, info, 37362306a36Sopenharmony_ci IPPROTO_UDP, use_cache); 37462306a36Sopenharmony_ci if (IS_ERR(dst)) 37562306a36Sopenharmony_ci return PTR_ERR(dst); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci skb_tunnel_check_pmtu(skb, dst, BAREUDP_IPV6_HLEN + info->options_len, 37862306a36Sopenharmony_ci false); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci sport = udp_flow_src_port(bareudp->net, skb, 38162306a36Sopenharmony_ci bareudp->sport_min, USHRT_MAX, 38262306a36Sopenharmony_ci true); 38362306a36Sopenharmony_ci prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); 38462306a36Sopenharmony_ci ttl = key->ttl; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci skb_scrub_packet(skb, xnet); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci err = -ENOSPC; 38962306a36Sopenharmony_ci if (!skb_pull(skb, skb_network_offset(skb))) 39062306a36Sopenharmony_ci goto free_dst; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + 39362306a36Sopenharmony_ci BAREUDP_BASE_HLEN + info->options_len + sizeof(struct ipv6hdr); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci err = skb_cow_head(skb, min_headroom); 39662306a36Sopenharmony_ci if (unlikely(err)) 39762306a36Sopenharmony_ci goto free_dst; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci err = udp_tunnel_handle_offloads(skb, udp_sum); 40062306a36Sopenharmony_ci if (err) 40162306a36Sopenharmony_ci goto free_dst; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci daddr = info->key.u.ipv6.dst; 40462306a36Sopenharmony_ci udp_tunnel6_xmit_skb(dst, sock->sk, skb, dev, 40562306a36Sopenharmony_ci &saddr, &daddr, prio, ttl, 40662306a36Sopenharmony_ci info->key.label, sport, bareudp->port, 40762306a36Sopenharmony_ci !(info->key.tun_flags & TUNNEL_CSUM)); 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cifree_dst: 41162306a36Sopenharmony_ci dst_release(dst); 41262306a36Sopenharmony_ci return err; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic bool bareudp_proto_valid(struct bareudp_dev *bareudp, __be16 proto) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci if (bareudp->ethertype == proto) 41862306a36Sopenharmony_ci return true; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!bareudp->multi_proto_mode) 42162306a36Sopenharmony_ci return false; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (bareudp->ethertype == htons(ETH_P_MPLS_UC) && 42462306a36Sopenharmony_ci proto == htons(ETH_P_MPLS_MC)) 42562306a36Sopenharmony_ci return true; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (bareudp->ethertype == htons(ETH_P_IP) && 42862306a36Sopenharmony_ci proto == htons(ETH_P_IPV6)) 42962306a36Sopenharmony_ci return true; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return false; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic netdev_tx_t bareudp_xmit(struct sk_buff *skb, struct net_device *dev) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 43762306a36Sopenharmony_ci struct ip_tunnel_info *info = NULL; 43862306a36Sopenharmony_ci int err; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!bareudp_proto_valid(bareudp, skb->protocol)) { 44162306a36Sopenharmony_ci err = -EINVAL; 44262306a36Sopenharmony_ci goto tx_error; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci info = skb_tunnel_info(skb); 44662306a36Sopenharmony_ci if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { 44762306a36Sopenharmony_ci err = -EINVAL; 44862306a36Sopenharmony_ci goto tx_error; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci rcu_read_lock(); 45262306a36Sopenharmony_ci if (ipv6_mod_enabled() && info->mode & IP_TUNNEL_INFO_IPV6) 45362306a36Sopenharmony_ci err = bareudp6_xmit_skb(skb, dev, bareudp, info); 45462306a36Sopenharmony_ci else 45562306a36Sopenharmony_ci err = bareudp_xmit_skb(skb, dev, bareudp, info); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci rcu_read_unlock(); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (likely(!err)) 46062306a36Sopenharmony_ci return NETDEV_TX_OK; 46162306a36Sopenharmony_citx_error: 46262306a36Sopenharmony_ci dev_kfree_skb(skb); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (err == -ELOOP) 46562306a36Sopenharmony_ci dev->stats.collisions++; 46662306a36Sopenharmony_ci else if (err == -ENETUNREACH) 46762306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci dev->stats.tx_errors++; 47062306a36Sopenharmony_ci return NETDEV_TX_OK; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int bareudp_fill_metadata_dst(struct net_device *dev, 47462306a36Sopenharmony_ci struct sk_buff *skb) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct ip_tunnel_info *info = skb_tunnel_info(skb); 47762306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 47862306a36Sopenharmony_ci bool use_cache; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci use_cache = ip_tunnel_dst_cache_usable(skb, info); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!ipv6_mod_enabled() || ip_tunnel_info_af(info) == AF_INET) { 48362306a36Sopenharmony_ci struct rtable *rt; 48462306a36Sopenharmony_ci __be32 saddr; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci rt = ip_route_output_tunnel(skb, dev, bareudp->net, &saddr, 48762306a36Sopenharmony_ci info, IPPROTO_UDP, use_cache); 48862306a36Sopenharmony_ci if (IS_ERR(rt)) 48962306a36Sopenharmony_ci return PTR_ERR(rt); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ip_rt_put(rt); 49262306a36Sopenharmony_ci info->key.u.ipv4.src = saddr; 49362306a36Sopenharmony_ci } else if (ip_tunnel_info_af(info) == AF_INET6) { 49462306a36Sopenharmony_ci struct dst_entry *dst; 49562306a36Sopenharmony_ci struct in6_addr saddr; 49662306a36Sopenharmony_ci struct socket *sock = rcu_dereference(bareudp->sock); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci dst = ip6_dst_lookup_tunnel(skb, dev, bareudp->net, sock, 49962306a36Sopenharmony_ci &saddr, info, IPPROTO_UDP, 50062306a36Sopenharmony_ci use_cache); 50162306a36Sopenharmony_ci if (IS_ERR(dst)) 50262306a36Sopenharmony_ci return PTR_ERR(dst); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci dst_release(dst); 50562306a36Sopenharmony_ci info->key.u.ipv6.src = saddr; 50662306a36Sopenharmony_ci } else { 50762306a36Sopenharmony_ci return -EINVAL; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci info->key.tp_src = udp_flow_src_port(bareudp->net, skb, 51162306a36Sopenharmony_ci bareudp->sport_min, 51262306a36Sopenharmony_ci USHRT_MAX, true); 51362306a36Sopenharmony_ci info->key.tp_dst = bareudp->port; 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic const struct net_device_ops bareudp_netdev_ops = { 51862306a36Sopenharmony_ci .ndo_init = bareudp_init, 51962306a36Sopenharmony_ci .ndo_uninit = bareudp_uninit, 52062306a36Sopenharmony_ci .ndo_open = bareudp_open, 52162306a36Sopenharmony_ci .ndo_stop = bareudp_stop, 52262306a36Sopenharmony_ci .ndo_start_xmit = bareudp_xmit, 52362306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 52462306a36Sopenharmony_ci .ndo_fill_metadata_dst = bareudp_fill_metadata_dst, 52562306a36Sopenharmony_ci}; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const struct nla_policy bareudp_policy[IFLA_BAREUDP_MAX + 1] = { 52862306a36Sopenharmony_ci [IFLA_BAREUDP_PORT] = { .type = NLA_U16 }, 52962306a36Sopenharmony_ci [IFLA_BAREUDP_ETHERTYPE] = { .type = NLA_U16 }, 53062306a36Sopenharmony_ci [IFLA_BAREUDP_SRCPORT_MIN] = { .type = NLA_U16 }, 53162306a36Sopenharmony_ci [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NLA_FLAG }, 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/* Info for udev, that this is a virtual tunnel endpoint */ 53562306a36Sopenharmony_cistatic const struct device_type bareudp_type = { 53662306a36Sopenharmony_ci .name = "bareudp", 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* Initialize the device structure. */ 54062306a36Sopenharmony_cistatic void bareudp_setup(struct net_device *dev) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci dev->netdev_ops = &bareudp_netdev_ops; 54362306a36Sopenharmony_ci dev->needs_free_netdev = true; 54462306a36Sopenharmony_ci SET_NETDEV_DEVTYPE(dev, &bareudp_type); 54562306a36Sopenharmony_ci dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; 54662306a36Sopenharmony_ci dev->features |= NETIF_F_RXCSUM; 54762306a36Sopenharmony_ci dev->features |= NETIF_F_LLTX; 54862306a36Sopenharmony_ci dev->features |= NETIF_F_GSO_SOFTWARE; 54962306a36Sopenharmony_ci dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; 55062306a36Sopenharmony_ci dev->hw_features |= NETIF_F_RXCSUM; 55162306a36Sopenharmony_ci dev->hw_features |= NETIF_F_GSO_SOFTWARE; 55262306a36Sopenharmony_ci dev->hard_header_len = 0; 55362306a36Sopenharmony_ci dev->addr_len = 0; 55462306a36Sopenharmony_ci dev->mtu = ETH_DATA_LEN; 55562306a36Sopenharmony_ci dev->min_mtu = IPV4_MIN_MTU; 55662306a36Sopenharmony_ci dev->max_mtu = IP_MAX_MTU - BAREUDP_BASE_HLEN; 55762306a36Sopenharmony_ci dev->type = ARPHRD_NONE; 55862306a36Sopenharmony_ci netif_keep_dst(dev); 55962306a36Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; 56062306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int bareudp_validate(struct nlattr *tb[], struct nlattr *data[], 56462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci if (!data) { 56762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 56862306a36Sopenharmony_ci "Not enough attributes provided to perform the operation"); 56962306a36Sopenharmony_ci return -EINVAL; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int bareudp2info(struct nlattr *data[], struct bareudp_conf *conf, 57562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci memset(conf, 0, sizeof(*conf)); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!data[IFLA_BAREUDP_PORT]) { 58062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "port not specified"); 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci if (!data[IFLA_BAREUDP_ETHERTYPE]) { 58462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ethertype not specified"); 58562306a36Sopenharmony_ci return -EINVAL; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci conf->port = nla_get_u16(data[IFLA_BAREUDP_PORT]); 58962306a36Sopenharmony_ci conf->ethertype = nla_get_u16(data[IFLA_BAREUDP_ETHERTYPE]); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (data[IFLA_BAREUDP_SRCPORT_MIN]) 59262306a36Sopenharmony_ci conf->sport_min = nla_get_u16(data[IFLA_BAREUDP_SRCPORT_MIN]); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (data[IFLA_BAREUDP_MULTIPROTO_MODE]) 59562306a36Sopenharmony_ci conf->multi_proto_mode = true; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic struct bareudp_dev *bareudp_find_dev(struct bareudp_net *bn, 60162306a36Sopenharmony_ci const struct bareudp_conf *conf) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct bareudp_dev *bareudp, *t = NULL; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci list_for_each_entry(bareudp, &bn->bareudp_list, next) { 60662306a36Sopenharmony_ci if (conf->port == bareudp->port) 60762306a36Sopenharmony_ci t = bareudp; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci return t; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int bareudp_configure(struct net *net, struct net_device *dev, 61362306a36Sopenharmony_ci struct bareudp_conf *conf, 61462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct bareudp_net *bn = net_generic(net, bareudp_net_id); 61762306a36Sopenharmony_ci struct bareudp_dev *t, *bareudp = netdev_priv(dev); 61862306a36Sopenharmony_ci int err; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci bareudp->net = net; 62162306a36Sopenharmony_ci bareudp->dev = dev; 62262306a36Sopenharmony_ci t = bareudp_find_dev(bn, conf); 62362306a36Sopenharmony_ci if (t) { 62462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Another bareudp device using the same port already exists"); 62562306a36Sopenharmony_ci return -EBUSY; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (conf->multi_proto_mode && 62962306a36Sopenharmony_ci (conf->ethertype != htons(ETH_P_MPLS_UC) && 63062306a36Sopenharmony_ci conf->ethertype != htons(ETH_P_IP))) { 63162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot set multiproto mode for this ethertype (only IPv4 and unicast MPLS are supported)"); 63262306a36Sopenharmony_ci return -EINVAL; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci bareudp->port = conf->port; 63662306a36Sopenharmony_ci bareudp->ethertype = conf->ethertype; 63762306a36Sopenharmony_ci bareudp->sport_min = conf->sport_min; 63862306a36Sopenharmony_ci bareudp->multi_proto_mode = conf->multi_proto_mode; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci err = register_netdevice(dev); 64162306a36Sopenharmony_ci if (err) 64262306a36Sopenharmony_ci return err; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci list_add(&bareudp->next, &bn->bareudp_list); 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic int bareudp_link_config(struct net_device *dev, 64962306a36Sopenharmony_ci struct nlattr *tb[]) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci int err; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (tb[IFLA_MTU]) { 65462306a36Sopenharmony_ci err = dev_set_mtu(dev, nla_get_u32(tb[IFLA_MTU])); 65562306a36Sopenharmony_ci if (err) 65662306a36Sopenharmony_ci return err; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic void bareudp_dellink(struct net_device *dev, struct list_head *head) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci list_del(&bareudp->next); 66662306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int bareudp_newlink(struct net *net, struct net_device *dev, 67062306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 67162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct bareudp_conf conf; 67462306a36Sopenharmony_ci int err; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci err = bareudp2info(data, &conf, extack); 67762306a36Sopenharmony_ci if (err) 67862306a36Sopenharmony_ci return err; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci err = bareudp_configure(net, dev, &conf, extack); 68162306a36Sopenharmony_ci if (err) 68262306a36Sopenharmony_ci return err; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci err = bareudp_link_config(dev, tb); 68562306a36Sopenharmony_ci if (err) 68662306a36Sopenharmony_ci goto err_unconfig; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cierr_unconfig: 69162306a36Sopenharmony_ci bareudp_dellink(dev, NULL); 69262306a36Sopenharmony_ci return err; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic size_t bareudp_get_size(const struct net_device *dev) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci return nla_total_size(sizeof(__be16)) + /* IFLA_BAREUDP_PORT */ 69862306a36Sopenharmony_ci nla_total_size(sizeof(__be16)) + /* IFLA_BAREUDP_ETHERTYPE */ 69962306a36Sopenharmony_ci nla_total_size(sizeof(__u16)) + /* IFLA_BAREUDP_SRCPORT_MIN */ 70062306a36Sopenharmony_ci nla_total_size(0) + /* IFLA_BAREUDP_MULTIPROTO_MODE */ 70162306a36Sopenharmony_ci 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int bareudp_fill_info(struct sk_buff *skb, const struct net_device *dev) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct bareudp_dev *bareudp = netdev_priv(dev); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (nla_put_be16(skb, IFLA_BAREUDP_PORT, bareudp->port)) 70962306a36Sopenharmony_ci goto nla_put_failure; 71062306a36Sopenharmony_ci if (nla_put_be16(skb, IFLA_BAREUDP_ETHERTYPE, bareudp->ethertype)) 71162306a36Sopenharmony_ci goto nla_put_failure; 71262306a36Sopenharmony_ci if (nla_put_u16(skb, IFLA_BAREUDP_SRCPORT_MIN, bareudp->sport_min)) 71362306a36Sopenharmony_ci goto nla_put_failure; 71462306a36Sopenharmony_ci if (bareudp->multi_proto_mode && 71562306a36Sopenharmony_ci nla_put_flag(skb, IFLA_BAREUDP_MULTIPROTO_MODE)) 71662306a36Sopenharmony_ci goto nla_put_failure; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cinla_put_failure: 72162306a36Sopenharmony_ci return -EMSGSIZE; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic struct rtnl_link_ops bareudp_link_ops __read_mostly = { 72562306a36Sopenharmony_ci .kind = "bareudp", 72662306a36Sopenharmony_ci .maxtype = IFLA_BAREUDP_MAX, 72762306a36Sopenharmony_ci .policy = bareudp_policy, 72862306a36Sopenharmony_ci .priv_size = sizeof(struct bareudp_dev), 72962306a36Sopenharmony_ci .setup = bareudp_setup, 73062306a36Sopenharmony_ci .validate = bareudp_validate, 73162306a36Sopenharmony_ci .newlink = bareudp_newlink, 73262306a36Sopenharmony_ci .dellink = bareudp_dellink, 73362306a36Sopenharmony_ci .get_size = bareudp_get_size, 73462306a36Sopenharmony_ci .fill_info = bareudp_fill_info, 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic __net_init int bareudp_init_net(struct net *net) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct bareudp_net *bn = net_generic(net, bareudp_net_id); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci INIT_LIST_HEAD(&bn->bareudp_list); 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic void bareudp_destroy_tunnels(struct net *net, struct list_head *head) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct bareudp_net *bn = net_generic(net, bareudp_net_id); 74862306a36Sopenharmony_ci struct bareudp_dev *bareudp, *next; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci list_for_each_entry_safe(bareudp, next, &bn->bareudp_list, next) 75162306a36Sopenharmony_ci unregister_netdevice_queue(bareudp->dev, head); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void __net_exit bareudp_exit_batch_net(struct list_head *net_list) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct net *net; 75762306a36Sopenharmony_ci LIST_HEAD(list); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci rtnl_lock(); 76062306a36Sopenharmony_ci list_for_each_entry(net, net_list, exit_list) 76162306a36Sopenharmony_ci bareudp_destroy_tunnels(net, &list); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* unregister the devices gathered above */ 76462306a36Sopenharmony_ci unregister_netdevice_many(&list); 76562306a36Sopenharmony_ci rtnl_unlock(); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic struct pernet_operations bareudp_net_ops = { 76962306a36Sopenharmony_ci .init = bareudp_init_net, 77062306a36Sopenharmony_ci .exit_batch = bareudp_exit_batch_net, 77162306a36Sopenharmony_ci .id = &bareudp_net_id, 77262306a36Sopenharmony_ci .size = sizeof(struct bareudp_net), 77362306a36Sopenharmony_ci}; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int __init bareudp_init_module(void) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci int rc; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci rc = register_pernet_subsys(&bareudp_net_ops); 78062306a36Sopenharmony_ci if (rc) 78162306a36Sopenharmony_ci goto out1; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci rc = rtnl_link_register(&bareudp_link_ops); 78462306a36Sopenharmony_ci if (rc) 78562306a36Sopenharmony_ci goto out2; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ciout2: 78962306a36Sopenharmony_ci unregister_pernet_subsys(&bareudp_net_ops); 79062306a36Sopenharmony_ciout1: 79162306a36Sopenharmony_ci return rc; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_cilate_initcall(bareudp_init_module); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void __exit bareudp_cleanup_module(void) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci rtnl_link_unregister(&bareudp_link_ops); 79862306a36Sopenharmony_ci unregister_pernet_subsys(&bareudp_net_ops); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_cimodule_exit(bareudp_cleanup_module); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("bareudp"); 80362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 80462306a36Sopenharmony_ciMODULE_AUTHOR("Martin Varghese <martin.varghese@nokia.com>"); 80562306a36Sopenharmony_ciMODULE_DESCRIPTION("Interface driver for UDP encapsulated traffic"); 806