4832 lines · 121.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * VXLAN: Virtual eXtensible Local Area Network * * Copyright (c) 2012-2013 Vyatta Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/udp.h>#include <linux/igmp.h>#include <linux/if_ether.h>#include <linux/ethtool.h>#include <net/arp.h>#include <net/ndisc.h>#include <net/ipv6_stubs.h>#include <net/ip.h>#include <net/icmp.h>#include <net/rtnetlink.h>#include <net/inet_ecn.h>#include <net/net_namespace.h>#include <net/netns/generic.h>#include <net/tun_proto.h>#include <net/vxlan.h>#include <net/nexthop.h> #if IS_ENABLED(CONFIG_IPV6)#include <net/ip6_tunnel.h>#include <net/ip6_checksum.h>#endif #define VXLAN_VERSION	"0.1" #define PORT_HASH_BITS	8#define PORT_HASH_SIZE  (1<<PORT_HASH_BITS)#define FDB_AGE_DEFAULT 300 /* 5 min */#define FDB_AGE_INTERVAL (10 * HZ)	/* rescan interval */ /* UDP port for VXLAN traffic. * The IANA assigned port is 4789, but the Linux default is 8472 * for compatibility with early adopters. */static unsigned short vxlan_port __read_mostly = 8472;module_param_named(udp_port, vxlan_port, ushort, 0444);MODULE_PARM_DESC(udp_port, "Destination UDP port"); static bool log_ecn_error = true;module_param(log_ecn_error, bool, 0644);MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); static unsigned int vxlan_net_id;static struct rtnl_link_ops vxlan_link_ops; static const u8 all_zeros_mac[ETH_ALEN + 2]; static int vxlan_sock_add(struct vxlan_dev *vxlan); static void vxlan_vs_del_dev(struct vxlan_dev *vxlan); /* per-network namespace private data for this module */struct vxlan_net {	struct list_head  vxlan_list;	struct hlist_head sock_list[PORT_HASH_SIZE];	spinlock_t	  sock_lock;	struct notifier_block nexthop_notifier_block;}; /* Forwarding table entry */struct vxlan_fdb {	struct hlist_node hlist;	/* linked list of entries */	struct rcu_head	  rcu;	unsigned long	  updated;	/* jiffies */	unsigned long	  used;	struct list_head  remotes;	u8		  eth_addr[ETH_ALEN];	u16		  state;	/* see ndm_state */	__be32		  vni;	u16		  flags;	/* see ndm_flags and below */	struct list_head  nh_list;	struct nexthop __rcu *nh;	struct vxlan_dev  __rcu *vdev;}; #define NTF_VXLAN_ADDED_BY_USER 0x100 /* salt for hash table */static u32 vxlan_salt __read_mostly; static inline bool vxlan_collect_metadata(struct vxlan_sock *vs){	return vs->flags & VXLAN_F_COLLECT_METADATA ||	       ip_tunnel_collect_metadata();} #if IS_ENABLED(CONFIG_IPV6)static inlinebool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b){	if (a->sa.sa_family != b->sa.sa_family)		return false;	if (a->sa.sa_family == AF_INET6)		return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);	else		return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;} static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla){	if (nla_len(nla) >= sizeof(struct in6_addr)) {		ip->sin6.sin6_addr = nla_get_in6_addr(nla);		ip->sa.sa_family = AF_INET6;		return 0;	} else if (nla_len(nla) >= sizeof(__be32)) {		ip->sin.sin_addr.s_addr = nla_get_in_addr(nla);		ip->sa.sa_family = AF_INET;		return 0;	} else {		return -EAFNOSUPPORT;	}} static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,			      const union vxlan_addr *ip){	if (ip->sa.sa_family == AF_INET6)		return nla_put_in6_addr(skb, attr, &ip->sin6.sin6_addr);	else		return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr);} #else /* !CONFIG_IPV6 */ static inlinebool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b){	return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;} static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla){	if (nla_len(nla) >= sizeof(struct in6_addr)) {		return -EAFNOSUPPORT;	} else if (nla_len(nla) >= sizeof(__be32)) {		ip->sin.sin_addr.s_addr = nla_get_in_addr(nla);		ip->sa.sa_family = AF_INET;		return 0;	} else {		return -EAFNOSUPPORT;	}} static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,			      const union vxlan_addr *ip){	return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr);}#endif /* Virtual Network hash table head */static inline struct hlist_head *vni_head(struct vxlan_sock *vs, __be32 vni){	return &vs->vni_list[hash_32((__force u32)vni, VNI_HASH_BITS)];} /* Socket hash table head */static inline struct hlist_head *vs_head(struct net *net, __be16 port){	struct vxlan_net *vn = net_generic(net, vxlan_net_id); 	return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];} /* First remote destination for a forwarding entry. * Guaranteed to be non-NULL because remotes are never deleted. */static inline struct vxlan_rdst *first_remote_rcu(struct vxlan_fdb *fdb){	if (rcu_access_pointer(fdb->nh))		return NULL;	return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list);} static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb){	if (rcu_access_pointer(fdb->nh))		return NULL;	return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);} /* Find VXLAN socket based on network namespace, address family, UDP port, * enabled unshareable flags and socket device binding (see l3mdev with * non-default VRF). */static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,					  __be16 port, u32 flags, int ifindex){	struct vxlan_sock *vs; 	flags &= VXLAN_F_RCV_FLAGS; 	hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {		if (inet_sk(vs->sock->sk)->inet_sport == port &&		    vxlan_get_sk_family(vs) == family &&		    vs->flags == flags &&		    vs->sock->sk->sk_bound_dev_if == ifindex)			return vs;	}	return NULL;} static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, int ifindex,					   __be32 vni){	struct vxlan_dev_node *node; 	/* For flow based devices, map all packets to VNI 0 */	if (vs->flags & VXLAN_F_COLLECT_METADATA)		vni = 0; 	hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {		if (node->vxlan->default_dst.remote_vni != vni)			continue; 		if (IS_ENABLED(CONFIG_IPV6)) {			const struct vxlan_config *cfg = &node->vxlan->cfg; 			if ((cfg->flags & VXLAN_F_IPV6_LINKLOCAL) &&			    cfg->remote_ifindex != ifindex)				continue;		} 		return node->vxlan;	} 	return NULL;} /* Look up VNI in a per net namespace table */static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex,					__be32 vni, sa_family_t family,					__be16 port, u32 flags){	struct vxlan_sock *vs; 	vs = vxlan_find_sock(net, family, port, flags, ifindex);	if (!vs)		return NULL; 	return vxlan_vs_find_vni(vs, ifindex, vni);} /* Fill in neighbour message in skbuff. */static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,			  const struct vxlan_fdb *fdb,			  u32 portid, u32 seq, int type, unsigned int flags,			  const struct vxlan_rdst *rdst){	unsigned long now = jiffies;	struct nda_cacheinfo ci;	bool send_ip, send_eth;	struct nlmsghdr *nlh;	struct nexthop *nh;	struct ndmsg *ndm;	int nh_family;	u32 nh_id; 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);	if (nlh == NULL)		return -EMSGSIZE; 	ndm = nlmsg_data(nlh);	memset(ndm, 0, sizeof(*ndm)); 	send_eth = send_ip = true; 	rcu_read_lock();	nh = rcu_dereference(fdb->nh);	if (nh) {		nh_family = nexthop_get_family(nh);		nh_id = nh->id;	}	rcu_read_unlock(); 	if (type == RTM_GETNEIGH) {		if (rdst) {			send_ip = !vxlan_addr_any(&rdst->remote_ip);			ndm->ndm_family = send_ip ? rdst->remote_ip.sa.sa_family : AF_INET;		} else if (nh) {			ndm->ndm_family = nh_family;		}		send_eth = !is_zero_ether_addr(fdb->eth_addr);	} else		ndm->ndm_family	= AF_BRIDGE;	ndm->ndm_state = fdb->state;	ndm->ndm_ifindex = vxlan->dev->ifindex;	ndm->ndm_flags = fdb->flags;	if (rdst && rdst->offloaded)		ndm->ndm_flags |= NTF_OFFLOADED;	ndm->ndm_type = RTN_UNICAST; 	if (!net_eq(dev_net(vxlan->dev), vxlan->net) &&	    nla_put_s32(skb, NDA_LINK_NETNSID,			peernet2id(dev_net(vxlan->dev), vxlan->net)))		goto nla_put_failure; 	if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr))		goto nla_put_failure;	if (nh) {		if (nla_put_u32(skb, NDA_NH_ID, nh_id))			goto nla_put_failure;	} else if (rdst) {		if (send_ip && vxlan_nla_put_addr(skb, NDA_DST,						  &rdst->remote_ip))			goto nla_put_failure; 		if (rdst->remote_port &&		    rdst->remote_port != vxlan->cfg.dst_port &&		    nla_put_be16(skb, NDA_PORT, rdst->remote_port))			goto nla_put_failure;		if (rdst->remote_vni != vxlan->default_dst.remote_vni &&		    nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))			goto nla_put_failure;		if (rdst->remote_ifindex &&		    nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))			goto nla_put_failure;	} 	if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && fdb->vni &&	    nla_put_u32(skb, NDA_SRC_VNI,			be32_to_cpu(fdb->vni)))		goto nla_put_failure; 	ci.ndm_used	 = jiffies_to_clock_t(now - fdb->used);	ci.ndm_confirmed = 0;	ci.ndm_updated	 = jiffies_to_clock_t(now - fdb->updated);	ci.ndm_refcnt	 = 0; 	if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))		goto nla_put_failure; 	nlmsg_end(skb, nlh);	return 0; nla_put_failure:	nlmsg_cancel(skb, nlh);	return -EMSGSIZE;} static inline size_t vxlan_nlmsg_size(void){	return NLMSG_ALIGN(sizeof(struct ndmsg))		+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */		+ nla_total_size(sizeof(struct in6_addr)) /* NDA_DST */		+ nla_total_size(sizeof(__be16)) /* NDA_PORT */		+ nla_total_size(sizeof(__be32)) /* NDA_VNI */		+ nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */		+ nla_total_size(sizeof(__s32)) /* NDA_LINK_NETNSID */		+ nla_total_size(sizeof(struct nda_cacheinfo));} static void __vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,			       struct vxlan_rdst *rd, int type){	struct net *net = dev_net(vxlan->dev);	struct sk_buff *skb;	int err = -ENOBUFS; 	skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC);	if (skb == NULL)		goto errout; 	err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, rd);	if (err < 0) {		/* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */		WARN_ON(err == -EMSGSIZE);		kfree_skb(skb);		goto errout;	} 	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);	return;errout:	if (err < 0)		rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);} static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,			    const struct vxlan_fdb *fdb,			    const struct vxlan_rdst *rd,			    struct netlink_ext_ack *extack,			    struct switchdev_notifier_vxlan_fdb_info *fdb_info){	fdb_info->info.dev = vxlan->dev;	fdb_info->info.extack = extack;	fdb_info->remote_ip = rd->remote_ip;	fdb_info->remote_port = rd->remote_port;	fdb_info->remote_vni = rd->remote_vni;	fdb_info->remote_ifindex = rd->remote_ifindex;	memcpy(fdb_info->eth_addr, fdb->eth_addr, ETH_ALEN);	fdb_info->vni = fdb->vni;	fdb_info->offloaded = rd->offloaded;	fdb_info->added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER;} static int vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,					      struct vxlan_fdb *fdb,					      struct vxlan_rdst *rd,					      bool adding,					      struct netlink_ext_ack *extack){	struct switchdev_notifier_vxlan_fdb_info info;	enum switchdev_notifier_type notifier_type;	int ret; 	if (WARN_ON(!rd))		return 0; 	notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE			       : SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE;	vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, NULL, &info);	ret = call_switchdev_notifiers(notifier_type, vxlan->dev,				       &info.info, extack);	return notifier_to_errno(ret);} static int vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,			    struct vxlan_rdst *rd, int type, bool swdev_notify,			    struct netlink_ext_ack *extack){	int err; 	if (swdev_notify && rd) {		switch (type) {		case RTM_NEWNEIGH:			err = vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,								 true, extack);			if (err)				return err;			break;		case RTM_DELNEIGH:			vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,							   false, extack);			break;		}	} 	__vxlan_fdb_notify(vxlan, fdb, rd, type);	return 0;} static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa){	struct vxlan_dev *vxlan = netdev_priv(dev);	struct vxlan_fdb f = {		.state = NUD_STALE,	};	struct vxlan_rdst remote = {		.remote_ip = *ipa, /* goes to NDA_DST */		.remote_vni = cpu_to_be32(VXLAN_N_VID),	}; 	vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true, NULL);} static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN]){	struct vxlan_fdb f = {		.state = NUD_STALE,	};	struct vxlan_rdst remote = { }; 	memcpy(f.eth_addr, eth_addr, ETH_ALEN); 	vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true, NULL);} /* Hash Ethernet address */static u32 eth_hash(const unsigned char *addr){	u64 value = get_unaligned((u64 *)addr); 	/* only want 6 bytes */#ifdef __BIG_ENDIAN	value >>= 16;#else	value <<= 16;#endif	return hash_64(value, FDB_HASH_BITS);} static u32 eth_vni_hash(const unsigned char *addr, __be32 vni){	/* use 1 byte of OUI and 3 bytes of NIC */	u32 key = get_unaligned((u32 *)(addr + 2)); 	return jhash_2words(key, vni, vxlan_salt) & (FDB_HASH_SIZE - 1);

Lines 1–500 of 4832.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t