|
@@ -106,6 +106,8 @@ struct vxlan_dev {
|
|
|
__be32 gaddr; /* multicast group */
|
|
|
__be32 saddr; /* source address */
|
|
|
unsigned int link; /* link to multicast over */
|
|
|
+ __u16 port_min; /* source port range */
|
|
|
+ __u16 port_max;
|
|
|
__u8 tos; /* TOS override */
|
|
|
__u8 ttl;
|
|
|
bool learn;
|
|
@@ -654,12 +656,29 @@ static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb)
|
|
|
skb->destructor = vxlan_sock_free;
|
|
|
}
|
|
|
|
|
|
+/* Compute source port for outgoing packet
|
|
|
+ * first choice to use L4 flow hash since it will spread
|
|
|
+ * better and maybe available from hardware
|
|
|
+ * secondary choice is to use jhash on the Ethernet header
|
|
|
+ */
|
|
|
+static u16 vxlan_src_port(const struct vxlan_dev *vxlan, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ unsigned int range = (vxlan->port_max - vxlan->port_min) + 1;
|
|
|
+ u32 hash;
|
|
|
+
|
|
|
+ hash = skb_get_rxhash(skb);
|
|
|
+ if (!hash)
|
|
|
+ hash = jhash(skb->data, 2 * ETH_ALEN,
|
|
|
+ (__force u32) skb->protocol);
|
|
|
+
|
|
|
+ return (((u64) hash * range) >> 32) + vxlan->port_min;
|
|
|
+}
|
|
|
+
|
|
|
/* Transmit local packets over Vxlan
|
|
|
*
|
|
|
* Outer IP header inherits ECN and DF from inner header.
|
|
|
* Outer UDP destination is the VXLAN assigned port.
|
|
|
- * source port is based on hash of flow if available
|
|
|
- * otherwise use a random value
|
|
|
+ * source port is based on hash of flow
|
|
|
*/
|
|
|
static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
{
|
|
@@ -671,8 +690,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
struct udphdr *uh;
|
|
|
struct flowi4 fl4;
|
|
|
unsigned int pkt_len = skb->len;
|
|
|
- u32 hash;
|
|
|
__be32 dst;
|
|
|
+ __u16 src_port;
|
|
|
__be16 df = 0;
|
|
|
__u8 tos, ttl;
|
|
|
int err;
|
|
@@ -695,7 +714,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
if (tos == 1)
|
|
|
tos = vxlan_get_dsfield(old_iph, skb);
|
|
|
|
|
|
- hash = skb_get_rxhash(skb);
|
|
|
+ src_port = vxlan_src_port(vxlan, skb);
|
|
|
|
|
|
memset(&fl4, 0, sizeof(fl4));
|
|
|
fl4.flowi4_oif = vxlan->link;
|
|
@@ -732,7 +751,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
uh = udp_hdr(skb);
|
|
|
|
|
|
uh->dest = htons(vxlan_port);
|
|
|
- uh->source = hash ? :random32();
|
|
|
+ uh->source = htons(src_port);
|
|
|
|
|
|
uh->len = htons(skb->len);
|
|
|
uh->check = 0;
|
|
@@ -960,6 +979,7 @@ static void vxlan_setup(struct net_device *dev)
|
|
|
{
|
|
|
struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
unsigned h;
|
|
|
+ int low, high;
|
|
|
|
|
|
eth_hw_addr_random(dev);
|
|
|
ether_setup(dev);
|
|
@@ -979,6 +999,10 @@ static void vxlan_setup(struct net_device *dev)
|
|
|
vxlan->age_timer.function = vxlan_cleanup;
|
|
|
vxlan->age_timer.data = (unsigned long) vxlan;
|
|
|
|
|
|
+ inet_get_local_port_range(&low, &high);
|
|
|
+ vxlan->port_min = low;
|
|
|
+ vxlan->port_max = high;
|
|
|
+
|
|
|
vxlan->dev = dev;
|
|
|
|
|
|
for (h = 0; h < FDB_HASH_SIZE; ++h)
|
|
@@ -995,6 +1019,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
|
|
|
[IFLA_VXLAN_LEARNING] = { .type = NLA_U8 },
|
|
|
[IFLA_VXLAN_AGEING] = { .type = NLA_U32 },
|
|
|
[IFLA_VXLAN_LIMIT] = { .type = NLA_U32 },
|
|
|
+ [IFLA_VXLAN_PORT_RANGE] = { .len = sizeof(struct ifla_vxlan_port_range) },
|
|
|
};
|
|
|
|
|
|
static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
|
|
@@ -1027,6 +1052,18 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
|
|
|
return -EADDRNOTAVAIL;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ if (data[IFLA_VXLAN_PORT_RANGE]) {
|
|
|
+ const struct ifla_vxlan_port_range *p
|
|
|
+ = nla_data(data[IFLA_VXLAN_PORT_RANGE]);
|
|
|
+
|
|
|
+ if (ntohs(p->high) < ntohs(p->low)) {
|
|
|
+ pr_debug("port range %u .. %u not valid\n",
|
|
|
+ ntohs(p->low), ntohs(p->high));
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1077,6 +1114,13 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
|
|
|
if (data[IFLA_VXLAN_LIMIT])
|
|
|
vxlan->addrmax = nla_get_u32(data[IFLA_VXLAN_LIMIT]);
|
|
|
|
|
|
+ if (data[IFLA_VXLAN_PORT_RANGE]) {
|
|
|
+ const struct ifla_vxlan_port_range *p
|
|
|
+ = nla_data(data[IFLA_VXLAN_PORT_RANGE]);
|
|
|
+ vxlan->port_min = ntohs(p->low);
|
|
|
+ vxlan->port_max = ntohs(p->high);
|
|
|
+ }
|
|
|
+
|
|
|
err = register_netdevice(dev);
|
|
|
if (!err)
|
|
|
hlist_add_head_rcu(&vxlan->hlist, vni_head(net, vxlan->vni));
|
|
@@ -1105,12 +1149,17 @@ static size_t vxlan_get_size(const struct net_device *dev)
|
|
|
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */
|
|
|
nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */
|
|
|
nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */
|
|
|
+ nla_total_size(sizeof(struct ifla_vxlan_port_range)) +
|
|
|
0;
|
|
|
}
|
|
|
|
|
|
static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|
|
{
|
|
|
const struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
|
+ struct ifla_vxlan_port_range ports = {
|
|
|
+ .low = htons(vxlan->port_min),
|
|
|
+ .high = htons(vxlan->port_max),
|
|
|
+ };
|
|
|
|
|
|
if (nla_put_u32(skb, IFLA_VXLAN_ID, vxlan->vni))
|
|
|
goto nla_put_failure;
|
|
@@ -1131,6 +1180,9 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|
|
nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
+ if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
nla_put_failure:
|