3734 lines · 93.5 KB · C
RawDownload
/* * VXLAN: Virtual eXtensible Local Area Network * * Copyright (c) 2012-2013 Vyatta Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #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/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/vxlan.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;}; /* 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;	u8		  flags;	/* see ndm_flags */}; /* 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 inline bool vxlan_addr_any(const union vxlan_addr *ipa){	if (ipa->sa.sa_family == AF_INET6)		return ipv6_addr_any(&ipa->sin6.sin6_addr);	else		return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);} static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa){	if (ipa->sa.sa_family == AF_INET6)		return ipv6_addr_is_multicast(&ipa->sin6.sin6_addr);	else		return IN_MULTICAST(ntohl(ipa->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 inline bool vxlan_addr_any(const union vxlan_addr *ipa){	return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);} static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa){	return IN_MULTICAST(ntohl(ipa->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){	return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list);} static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb){	return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);} /* Find VXLAN socket based on network namespace, address family and UDP port * and enabled unshareable flags. */static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,					  __be16 port, u32 flags){	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)			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);	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;	struct nlmsghdr *nlh;	struct ndmsg *ndm;	bool send_ip, send_eth; 	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; 	if (type == RTM_GETNEIGH) {		send_ip = !vxlan_addr_any(&rdst->remote_ip);		send_eth = !is_zero_ether_addr(fdb->eth_addr);		ndm->ndm_family = send_ip ? rdst->remote_ip.sa.sa_family : AF_INET;	} else		ndm->ndm_family	= AF_BRIDGE;	ndm->ndm_state = fdb->state;	ndm->ndm_ifindex = vxlan->dev->ifindex;	ndm->ndm_flags = fdb->flags;	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 (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 ((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;	if (rdst->remote_ifindex &&	    nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))		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_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);} 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);} /* 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);} /* Hash chain to use given mac address */static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan,						const u8 *mac, __be32 vni){	if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)		return &vxlan->fdb_head[eth_vni_hash(mac, vni)];	else		return &vxlan->fdb_head[eth_hash(mac)];} /* Look up Ethernet address in forwarding table */static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan,					  const u8 *mac, __be32 vni){	struct hlist_head *head = vxlan_fdb_head(vxlan, mac, vni);	struct vxlan_fdb *f; 	hlist_for_each_entry_rcu(f, head, hlist) {		if (ether_addr_equal(mac, f->eth_addr)) {			if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) {				if (vni == f->vni)					return f;			} else {				return f;			}		}	} 	return NULL;} static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,					const u8 *mac, __be32 vni){	struct vxlan_fdb *f; 	f = __vxlan_find_mac(vxlan, mac, vni);	if (f)		f->used = jiffies; 	return f;} /* caller should hold vxlan->hash_lock */static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f,					      union vxlan_addr *ip, __be16 port,					      __be32 vni, __u32 ifindex){	struct vxlan_rdst *rd; 	list_for_each_entry(rd, &f->remotes, list) {		if (vxlan_addr_equal(&rd->remote_ip, ip) &&		    rd->remote_port == port &&		    rd->remote_vni == vni &&		    rd->remote_ifindex == ifindex)			return rd;	} 	return NULL;} /* Replace destination of unicast mac */static int vxlan_fdb_replace(struct vxlan_fdb *f,			     union vxlan_addr *ip, __be16 port, __be32 vni,			     __u32 ifindex){	struct vxlan_rdst *rd; 	rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex);	if (rd)		return 0; 

Lines 1–500 of 3734.