diff options
author | David Stevens <dlstevens@us.ibm.com> | 2012-11-19 21:50:14 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-20 13:41:28 -0500 |
commit | e4f67addf158f98f8197e08974966b18480dc751 (patch) | |
tree | 01f1a199c7d875c89d3b8f4345ec3d0f03d0cbe7 | |
parent | ff33c0e1885cda44dd14c79f70df4706f83582a0 (diff) |
add DOVE extensions for VXLAN
This patch provides extensions to VXLAN for supporting Distributed
Overlay Virtual Ethernet (DOVE) networks. The patch includes:
+ a dove flag per VXLAN device to enable DOVE extensions
+ ARP reduction, whereby a bridge-connected VXLAN tunnel endpoint
answers ARP requests from the local bridge on behalf of
remote DOVE clients
+ route short-circuiting (aka L3 switching). Known destination IP
addresses use the corresponding destination MAC address for
switching rather than going to a (possibly remote) router first.
+ netlink notification messages for forwarding table and L3 switching
misses
Changes since v2
- combined bools into "u32 flags"
- replaced loop with !is_zero_ether_addr()
Signed-off-by: David L Stevens <dlstevens@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/vxlan.c | 256 | ||||
-rw-r--r-- | include/uapi/linux/if_link.h | 4 |
2 files changed, 235 insertions, 25 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index a14df1ce99ff..ce77b8b693ae 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c | |||
@@ -29,6 +29,8 @@ | |||
29 | #include <linux/etherdevice.h> | 29 | #include <linux/etherdevice.h> |
30 | #include <linux/if_ether.h> | 30 | #include <linux/if_ether.h> |
31 | #include <linux/hash.h> | 31 | #include <linux/hash.h> |
32 | #include <net/arp.h> | ||
33 | #include <net/ndisc.h> | ||
32 | #include <net/ip.h> | 34 | #include <net/ip.h> |
33 | #include <net/icmp.h> | 35 | #include <net/icmp.h> |
34 | #include <net/udp.h> | 36 | #include <net/udp.h> |
@@ -110,7 +112,7 @@ struct vxlan_dev { | |||
110 | __u16 port_max; | 112 | __u16 port_max; |
111 | __u8 tos; /* TOS override */ | 113 | __u8 tos; /* TOS override */ |
112 | __u8 ttl; | 114 | __u8 ttl; |
113 | bool learn; | 115 | u32 flags; /* VXLAN_F_* below */ |
114 | 116 | ||
115 | unsigned long age_interval; | 117 | unsigned long age_interval; |
116 | struct timer_list age_timer; | 118 | struct timer_list age_timer; |
@@ -121,6 +123,12 @@ struct vxlan_dev { | |||
121 | struct hlist_head fdb_head[FDB_HASH_SIZE]; | 123 | struct hlist_head fdb_head[FDB_HASH_SIZE]; |
122 | }; | 124 | }; |
123 | 125 | ||
126 | #define VXLAN_F_LEARN 0x01 | ||
127 | #define VXLAN_F_PROXY 0x02 | ||
128 | #define VXLAN_F_RSC 0x04 | ||
129 | #define VXLAN_F_L2MISS 0x08 | ||
130 | #define VXLAN_F_L3MISS 0x10 | ||
131 | |||
124 | /* salt for hash table */ | 132 | /* salt for hash table */ |
125 | static u32 vxlan_salt __read_mostly; | 133 | static u32 vxlan_salt __read_mostly; |
126 | 134 | ||
@@ -154,6 +162,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, | |||
154 | struct nda_cacheinfo ci; | 162 | struct nda_cacheinfo ci; |
155 | struct nlmsghdr *nlh; | 163 | struct nlmsghdr *nlh; |
156 | struct ndmsg *ndm; | 164 | struct ndmsg *ndm; |
165 | bool send_ip, send_eth; | ||
157 | 166 | ||
158 | nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); | 167 | nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); |
159 | if (nlh == NULL) | 168 | if (nlh == NULL) |
@@ -161,16 +170,24 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, | |||
161 | 170 | ||
162 | ndm = nlmsg_data(nlh); | 171 | ndm = nlmsg_data(nlh); |
163 | memset(ndm, 0, sizeof(*ndm)); | 172 | memset(ndm, 0, sizeof(*ndm)); |
164 | ndm->ndm_family = AF_BRIDGE; | 173 | |
174 | send_eth = send_ip = true; | ||
175 | |||
176 | if (type == RTM_GETNEIGH) { | ||
177 | ndm->ndm_family = AF_INET; | ||
178 | send_ip = fdb->remote_ip != 0; | ||
179 | send_eth = !is_zero_ether_addr(fdb->eth_addr); | ||
180 | } else | ||
181 | ndm->ndm_family = AF_BRIDGE; | ||
165 | ndm->ndm_state = fdb->state; | 182 | ndm->ndm_state = fdb->state; |
166 | ndm->ndm_ifindex = vxlan->dev->ifindex; | 183 | ndm->ndm_ifindex = vxlan->dev->ifindex; |
167 | ndm->ndm_flags = NTF_SELF; | 184 | ndm->ndm_flags = NTF_SELF; |
168 | ndm->ndm_type = NDA_DST; | 185 | ndm->ndm_type = NDA_DST; |
169 | 186 | ||
170 | if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr)) | 187 | if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr)) |
171 | goto nla_put_failure; | 188 | goto nla_put_failure; |
172 | 189 | ||
173 | if (nla_put_be32(skb, NDA_DST, fdb->remote_ip)) | 190 | if (send_ip && nla_put_be32(skb, NDA_DST, fdb->remote_ip)) |
174 | goto nla_put_failure; | 191 | goto nla_put_failure; |
175 | 192 | ||
176 | ci.ndm_used = jiffies_to_clock_t(now - fdb->used); | 193 | ci.ndm_used = jiffies_to_clock_t(now - fdb->used); |
@@ -222,6 +239,29 @@ errout: | |||
222 | rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); | 239 | rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); |
223 | } | 240 | } |
224 | 241 | ||
242 | static void vxlan_ip_miss(struct net_device *dev, __be32 ipa) | ||
243 | { | ||
244 | struct vxlan_dev *vxlan = netdev_priv(dev); | ||
245 | struct vxlan_fdb f; | ||
246 | |||
247 | memset(&f, 0, sizeof f); | ||
248 | f.state = NUD_STALE; | ||
249 | f.remote_ip = ipa; /* goes to NDA_DST */ | ||
250 | |||
251 | vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH); | ||
252 | } | ||
253 | |||
254 | static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN]) | ||
255 | { | ||
256 | struct vxlan_fdb f; | ||
257 | |||
258 | memset(&f, 0, sizeof f); | ||
259 | f.state = NUD_STALE; | ||
260 | memcpy(f.eth_addr, eth_addr, ETH_ALEN); | ||
261 | |||
262 | vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH); | ||
263 | } | ||
264 | |||
225 | /* Hash Ethernet address */ | 265 | /* Hash Ethernet address */ |
226 | static u32 eth_hash(const unsigned char *addr) | 266 | static u32 eth_hash(const unsigned char *addr) |
227 | { | 267 | { |
@@ -551,6 +591,8 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) | |||
551 | goto drop; | 591 | goto drop; |
552 | } | 592 | } |
553 | 593 | ||
594 | skb_reset_mac_header(skb); | ||
595 | |||
554 | /* Re-examine inner Ethernet packet */ | 596 | /* Re-examine inner Ethernet packet */ |
555 | oip = ip_hdr(skb); | 597 | oip = ip_hdr(skb); |
556 | skb->protocol = eth_type_trans(skb, vxlan->dev); | 598 | skb->protocol = eth_type_trans(skb, vxlan->dev); |
@@ -560,7 +602,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) | |||
560 | vxlan->dev->dev_addr) == 0) | 602 | vxlan->dev->dev_addr) == 0) |
561 | goto drop; | 603 | goto drop; |
562 | 604 | ||
563 | if (vxlan->learn) | 605 | if (vxlan->flags & VXLAN_F_LEARN) |
564 | vxlan_snoop(skb->dev, oip->saddr, eth_hdr(skb)->h_source); | 606 | vxlan_snoop(skb->dev, oip->saddr, eth_hdr(skb)->h_source); |
565 | 607 | ||
566 | __skb_tunnel_rx(skb, vxlan->dev); | 608 | __skb_tunnel_rx(skb, vxlan->dev); |
@@ -599,6 +641,117 @@ drop: | |||
599 | return 0; | 641 | return 0; |
600 | } | 642 | } |
601 | 643 | ||
644 | static int arp_reduce(struct net_device *dev, struct sk_buff *skb) | ||
645 | { | ||
646 | struct vxlan_dev *vxlan = netdev_priv(dev); | ||
647 | struct arphdr *parp; | ||
648 | u8 *arpptr, *sha; | ||
649 | __be32 sip, tip; | ||
650 | struct neighbour *n; | ||
651 | |||
652 | if (dev->flags & IFF_NOARP) | ||
653 | goto out; | ||
654 | |||
655 | if (!pskb_may_pull(skb, arp_hdr_len(dev))) { | ||
656 | dev->stats.tx_dropped++; | ||
657 | goto out; | ||
658 | } | ||
659 | parp = arp_hdr(skb); | ||
660 | |||
661 | if ((parp->ar_hrd != htons(ARPHRD_ETHER) && | ||
662 | parp->ar_hrd != htons(ARPHRD_IEEE802)) || | ||
663 | parp->ar_pro != htons(ETH_P_IP) || | ||
664 | parp->ar_op != htons(ARPOP_REQUEST) || | ||
665 | parp->ar_hln != dev->addr_len || | ||
666 | parp->ar_pln != 4) | ||
667 | goto out; | ||
668 | arpptr = (u8 *)parp + sizeof(struct arphdr); | ||
669 | sha = arpptr; | ||
670 | arpptr += dev->addr_len; /* sha */ | ||
671 | memcpy(&sip, arpptr, sizeof(sip)); | ||
672 | arpptr += sizeof(sip); | ||
673 | arpptr += dev->addr_len; /* tha */ | ||
674 | memcpy(&tip, arpptr, sizeof(tip)); | ||
675 | |||
676 | if (ipv4_is_loopback(tip) || | ||
677 | ipv4_is_multicast(tip)) | ||
678 | goto out; | ||
679 | |||
680 | n = neigh_lookup(&arp_tbl, &tip, dev); | ||
681 | |||
682 | if (n) { | ||
683 | struct vxlan_dev *vxlan = netdev_priv(dev); | ||
684 | struct vxlan_fdb *f; | ||
685 | struct sk_buff *reply; | ||
686 | |||
687 | if (!(n->nud_state & NUD_CONNECTED)) { | ||
688 | neigh_release(n); | ||
689 | goto out; | ||
690 | } | ||
691 | |||
692 | f = vxlan_find_mac(vxlan, n->ha); | ||
693 | if (f && f->remote_ip == 0) { | ||
694 | /* bridge-local neighbor */ | ||
695 | neigh_release(n); | ||
696 | goto out; | ||
697 | } | ||
698 | |||
699 | reply = arp_create(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, | ||
700 | n->ha, sha); | ||
701 | |||
702 | neigh_release(n); | ||
703 | |||
704 | skb_reset_mac_header(reply); | ||
705 | __skb_pull(reply, skb_network_offset(reply)); | ||
706 | reply->ip_summed = CHECKSUM_UNNECESSARY; | ||
707 | reply->pkt_type = PACKET_HOST; | ||
708 | |||
709 | if (netif_rx_ni(reply) == NET_RX_DROP) | ||
710 | dev->stats.rx_dropped++; | ||
711 | } else if (vxlan->flags & VXLAN_F_L3MISS) | ||
712 | vxlan_ip_miss(dev, tip); | ||
713 | out: | ||
714 | consume_skb(skb); | ||
715 | return NETDEV_TX_OK; | ||
716 | } | ||
717 | |||
718 | static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) | ||
719 | { | ||
720 | struct vxlan_dev *vxlan = netdev_priv(dev); | ||
721 | struct neighbour *n; | ||
722 | struct iphdr *pip; | ||
723 | |||
724 | if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) | ||
725 | return false; | ||
726 | |||
727 | n = NULL; | ||
728 | switch (ntohs(eth_hdr(skb)->h_proto)) { | ||
729 | case ETH_P_IP: | ||
730 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
731 | return false; | ||
732 | pip = ip_hdr(skb); | ||
733 | n = neigh_lookup(&arp_tbl, &pip->daddr, dev); | ||
734 | break; | ||
735 | default: | ||
736 | return false; | ||
737 | } | ||
738 | |||
739 | if (n) { | ||
740 | bool diff; | ||
741 | |||
742 | diff = compare_ether_addr(eth_hdr(skb)->h_dest, n->ha) != 0; | ||
743 | if (diff) { | ||
744 | memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, | ||
745 | dev->addr_len); | ||
746 | memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); | ||
747 | } | ||
748 | neigh_release(n); | ||
749 | return diff; | ||
750 | } else if (vxlan->flags & VXLAN_F_L3MISS) | ||
751 | vxlan_ip_miss(dev, pip->daddr); | ||
752 | return false; | ||
753 | } | ||
754 | |||
602 | /* Extract dsfield from inner protocol */ | 755 | /* Extract dsfield from inner protocol */ |
603 | static inline u8 vxlan_get_dsfield(const struct iphdr *iph, | 756 | static inline u8 vxlan_get_dsfield(const struct iphdr *iph, |
604 | const struct sk_buff *skb) | 757 | const struct sk_buff *skb) |
@@ -621,22 +774,6 @@ static inline u8 vxlan_ecn_encap(u8 tos, | |||
621 | return INET_ECN_encapsulate(tos, inner); | 774 | return INET_ECN_encapsulate(tos, inner); |
622 | } | 775 | } |
623 | 776 | ||
624 | static __be32 vxlan_find_dst(struct vxlan_dev *vxlan, struct sk_buff *skb) | ||
625 | { | ||
626 | const struct ethhdr *eth = (struct ethhdr *) skb->data; | ||
627 | const struct vxlan_fdb *f; | ||
628 | |||
629 | if (is_multicast_ether_addr(eth->h_dest)) | ||
630 | return vxlan->gaddr; | ||
631 | |||
632 | f = vxlan_find_mac(vxlan, eth->h_dest); | ||
633 | if (f) | ||
634 | return f->remote_ip; | ||
635 | else | ||
636 | return vxlan->gaddr; | ||
637 | |||
638 | } | ||
639 | |||
640 | static void vxlan_sock_free(struct sk_buff *skb) | 777 | static void vxlan_sock_free(struct sk_buff *skb) |
641 | { | 778 | { |
642 | sock_put(skb->sk); | 779 | sock_put(skb->sk); |
@@ -683,6 +820,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) | |||
683 | struct vxlan_dev *vxlan = netdev_priv(dev); | 820 | struct vxlan_dev *vxlan = netdev_priv(dev); |
684 | struct rtable *rt; | 821 | struct rtable *rt; |
685 | const struct iphdr *old_iph; | 822 | const struct iphdr *old_iph; |
823 | struct ethhdr *eth; | ||
686 | struct iphdr *iph; | 824 | struct iphdr *iph; |
687 | struct vxlanhdr *vxh; | 825 | struct vxlanhdr *vxh; |
688 | struct udphdr *uh; | 826 | struct udphdr *uh; |
@@ -693,10 +831,50 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) | |||
693 | __be16 df = 0; | 831 | __be16 df = 0; |
694 | __u8 tos, ttl; | 832 | __u8 tos, ttl; |
695 | int err; | 833 | int err; |
834 | bool did_rsc = false; | ||
835 | const struct vxlan_fdb *f; | ||
836 | |||
837 | skb_reset_mac_header(skb); | ||
838 | eth = eth_hdr(skb); | ||
839 | |||
840 | if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP) | ||
841 | return arp_reduce(dev, skb); | ||
842 | else if ((vxlan->flags&VXLAN_F_RSC) && ntohs(eth->h_proto) == ETH_P_IP) | ||
843 | did_rsc = route_shortcircuit(dev, skb); | ||
696 | 844 | ||
697 | dst = vxlan_find_dst(vxlan, skb); | 845 | f = vxlan_find_mac(vxlan, eth->h_dest); |
698 | if (!dst) | 846 | if (f == NULL) { |
847 | did_rsc = false; | ||
848 | dst = vxlan->gaddr; | ||
849 | if (!dst && (vxlan->flags & VXLAN_F_L2MISS) && | ||
850 | !is_multicast_ether_addr(eth->h_dest)) | ||
851 | vxlan_fdb_miss(vxlan, eth->h_dest); | ||
852 | } else | ||
853 | dst = f->remote_ip; | ||
854 | |||
855 | if (!dst) { | ||
856 | if (did_rsc) { | ||
857 | __skb_pull(skb, skb_network_offset(skb)); | ||
858 | skb->ip_summed = CHECKSUM_NONE; | ||
859 | skb->pkt_type = PACKET_HOST; | ||
860 | |||
861 | /* short-circuited back to local bridge */ | ||
862 | if (netif_rx(skb) == NET_RX_SUCCESS) { | ||
863 | struct vxlan_stats *stats = | ||
864 | this_cpu_ptr(vxlan->stats); | ||
865 | |||
866 | u64_stats_update_begin(&stats->syncp); | ||
867 | stats->tx_packets++; | ||
868 | stats->tx_bytes += pkt_len; | ||
869 | u64_stats_update_end(&stats->syncp); | ||
870 | } else { | ||
871 | dev->stats.tx_errors++; | ||
872 | dev->stats.tx_aborted_errors++; | ||
873 | } | ||
874 | return NETDEV_TX_OK; | ||
875 | } | ||
699 | goto drop; | 876 | goto drop; |
877 | } | ||
700 | 878 | ||
701 | /* Need space for new headers (invalidates iph ptr) */ | 879 | /* Need space for new headers (invalidates iph ptr) */ |
702 | if (skb_cow_head(skb, VXLAN_HEADROOM)) | 880 | if (skb_cow_head(skb, VXLAN_HEADROOM)) |
@@ -1019,6 +1197,10 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { | |||
1019 | [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, | 1197 | [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, |
1020 | [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, | 1198 | [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, |
1021 | [IFLA_VXLAN_PORT_RANGE] = { .len = sizeof(struct ifla_vxlan_port_range) }, | 1199 | [IFLA_VXLAN_PORT_RANGE] = { .len = sizeof(struct ifla_vxlan_port_range) }, |
1200 | [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, | ||
1201 | [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, | ||
1202 | [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, | ||
1203 | [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, | ||
1022 | }; | 1204 | }; |
1023 | 1205 | ||
1024 | static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) | 1206 | static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) |
@@ -1114,13 +1296,25 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, | |||
1114 | vxlan->ttl = nla_get_u8(data[IFLA_VXLAN_TTL]); | 1296 | vxlan->ttl = nla_get_u8(data[IFLA_VXLAN_TTL]); |
1115 | 1297 | ||
1116 | if (!data[IFLA_VXLAN_LEARNING] || nla_get_u8(data[IFLA_VXLAN_LEARNING])) | 1298 | if (!data[IFLA_VXLAN_LEARNING] || nla_get_u8(data[IFLA_VXLAN_LEARNING])) |
1117 | vxlan->learn = true; | 1299 | vxlan->flags |= VXLAN_F_LEARN; |
1118 | 1300 | ||
1119 | if (data[IFLA_VXLAN_AGEING]) | 1301 | if (data[IFLA_VXLAN_AGEING]) |
1120 | vxlan->age_interval = nla_get_u32(data[IFLA_VXLAN_AGEING]); | 1302 | vxlan->age_interval = nla_get_u32(data[IFLA_VXLAN_AGEING]); |
1121 | else | 1303 | else |
1122 | vxlan->age_interval = FDB_AGE_DEFAULT; | 1304 | vxlan->age_interval = FDB_AGE_DEFAULT; |
1123 | 1305 | ||
1306 | if (data[IFLA_VXLAN_PROXY] && nla_get_u8(data[IFLA_VXLAN_PROXY])) | ||
1307 | vxlan->flags |= VXLAN_F_PROXY; | ||
1308 | |||
1309 | if (data[IFLA_VXLAN_RSC] && nla_get_u8(data[IFLA_VXLAN_RSC])) | ||
1310 | vxlan->flags |= VXLAN_F_RSC; | ||
1311 | |||
1312 | if (data[IFLA_VXLAN_L2MISS] && nla_get_u8(data[IFLA_VXLAN_L2MISS])) | ||
1313 | vxlan->flags |= VXLAN_F_L2MISS; | ||
1314 | |||
1315 | if (data[IFLA_VXLAN_L3MISS] && nla_get_u8(data[IFLA_VXLAN_L3MISS])) | ||
1316 | vxlan->flags |= VXLAN_F_L3MISS; | ||
1317 | |||
1124 | if (data[IFLA_VXLAN_LIMIT]) | 1318 | if (data[IFLA_VXLAN_LIMIT]) |
1125 | vxlan->addrmax = nla_get_u32(data[IFLA_VXLAN_LIMIT]); | 1319 | vxlan->addrmax = nla_get_u32(data[IFLA_VXLAN_LIMIT]); |
1126 | 1320 | ||
@@ -1157,6 +1351,10 @@ static size_t vxlan_get_size(const struct net_device *dev) | |||
1157 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TTL */ | 1351 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TTL */ |
1158 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TOS */ | 1352 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TOS */ |
1159 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */ | 1353 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */ |
1354 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_PROXY */ | ||
1355 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_RSC */ | ||
1356 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_L2MISS */ | ||
1357 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_L3MISS */ | ||
1160 | nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */ | 1358 | nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */ |
1161 | nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */ | 1359 | nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */ |
1162 | nla_total_size(sizeof(struct ifla_vxlan_port_range)) + | 1360 | nla_total_size(sizeof(struct ifla_vxlan_port_range)) + |
@@ -1185,7 +1383,15 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) | |||
1185 | 1383 | ||
1186 | if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->ttl) || | 1384 | if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->ttl) || |
1187 | nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->tos) || | 1385 | nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->tos) || |
1188 | nla_put_u8(skb, IFLA_VXLAN_LEARNING, vxlan->learn) || | 1386 | nla_put_u8(skb, IFLA_VXLAN_LEARNING, |
1387 | !!(vxlan->flags & VXLAN_F_LEARN)) || | ||
1388 | nla_put_u8(skb, IFLA_VXLAN_PROXY, | ||
1389 | !!(vxlan->flags & VXLAN_F_PROXY)) || | ||
1390 | nla_put_u8(skb, IFLA_VXLAN_RSC, !!(vxlan->flags & VXLAN_F_RSC)) || | ||
1391 | nla_put_u8(skb, IFLA_VXLAN_L2MISS, | ||
1392 | !!(vxlan->flags & VXLAN_F_L2MISS)) || | ||
1393 | nla_put_u8(skb, IFLA_VXLAN_L3MISS, | ||
1394 | !!(vxlan->flags & VXLAN_F_L3MISS)) || | ||
1189 | nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) || | 1395 | nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) || |
1190 | nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax)) | 1396 | nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax)) |
1191 | goto nla_put_failure; | 1397 | goto nla_put_failure; |
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 7aae0179ae44..bb58aeb7f34d 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h | |||
@@ -302,6 +302,10 @@ enum { | |||
302 | IFLA_VXLAN_AGEING, | 302 | IFLA_VXLAN_AGEING, |
303 | IFLA_VXLAN_LIMIT, | 303 | IFLA_VXLAN_LIMIT, |
304 | IFLA_VXLAN_PORT_RANGE, | 304 | IFLA_VXLAN_PORT_RANGE, |
305 | IFLA_VXLAN_PROXY, | ||
306 | IFLA_VXLAN_RSC, | ||
307 | IFLA_VXLAN_L2MISS, | ||
308 | IFLA_VXLAN_L3MISS, | ||
305 | __IFLA_VXLAN_MAX | 309 | __IFLA_VXLAN_MAX |
306 | }; | 310 | }; |
307 | #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) | 311 | #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) |