Commit 253eb98b authored by Stephen Hemminger's avatar Stephen Hemminger

Merge branch 'vxlan'

Conflicts:
	include/linux/if_link.h
parents 92905c6e 7f747fd9
......@@ -7,6 +7,7 @@ extern int print_fdb(const struct sockaddr_nl *who,
extern int do_fdb(int argc, char **argv);
extern int do_monitor(int argc, char **argv);
extern int preferred_family;
extern int show_stats;
extern int show_detail;
extern int timestamp;
......
......@@ -15,6 +15,7 @@
#include "br_common.h"
struct rtnl_handle rth = { .fd = -1 };
int preferred_family = AF_UNSPEC;
int resolve_hosts;
int show_stats;
int show_details;
......@@ -86,6 +87,23 @@ main(int argc, char **argv)
++show_details;
} else if (matches(opt, "-timestamp") == 0) {
++timestamp;
} else if (matches(opt, "-family") == 0) {
argc--;
argv++;
if (argc <= 1)
usage();
if (strcmp(argv[1], "inet") == 0)
preferred_family = AF_INET;
else if (strcmp(argv[1], "inet6") == 0)
preferred_family = AF_INET6;
else if (strcmp(argv[1], "help") == 0)
usage();
else
invarg("invalid protocol family", argv[1]);
} else if (strcmp(opt, "-4") == 0) {
preferred_family = AF_INET;
} else if (strcmp(opt, "-6") == 0) {
preferred_family = AF_INET6;
} else {
fprintf(stderr, "Option \"%s\" is unknown, try \"bridge help\".\n", opt);
exit(-1);
......
/*
* Get/set/delete fdb table with netlink
*
* TODO: merge/replace this with ip neighbour
*
* Authors: Stephen Hemminger <shemminger@vyatta.com>
*/
......@@ -20,13 +22,14 @@
#include "libnetlink.h"
#include "br_common.h"
#include "rt_names.h"
#include "utils.h"
int filter_index;
static void usage(void)
{
fprintf(stderr, "Usage: bridge fdb { add | del } ADDR dev DEV {self|master}\n");
fprintf(stderr, "Usage: bridge fdb { add | del } ADDR dev DEV {self|master} [ temp ] [ dst IPADDR]\n");
fprintf(stderr, " bridge fdb {show} [ dev DEV ]\n");
exit(-1);
}
......@@ -35,15 +38,15 @@ static const char *state_n2a(unsigned s)
{
static char buf[32];
if (s & NUD_PERMANENT)
return "local";
if (s & NUD_PERMANENT)
return "permanent";
if (s & NUD_NOARP)
return "static";
if (s & NUD_STALE)
return "stale";
if (s & NUD_REACHABLE)
return "";
......@@ -51,24 +54,19 @@ static const char *state_n2a(unsigned s)
return buf;
}
static char *fmt_time(char *b, size_t l, unsigned long tick)
{
static int hz;
if (hz == 0)
hz = __get_user_hz();
snprintf(b, l, "%lu.%02lu", tick / hz, ((tick % hz) * hz) / 100);
return b;
}
int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
{
FILE *fp = arg;
struct ndmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr * tb[NDA_MAX+1];
const __u8 *addr = NULL;
char b1[32];
if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
return 0;
}
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
......@@ -86,37 +84,49 @@ int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
if (n->nlmsg_type == RTM_DELNEIGH)
printf("Deleted ");
if (tb[NDA_LLADDR])
addr = RTA_DATA(tb[NDA_LLADDR]);
else {
fprintf(stderr, "missing lladdr\n");
return -1;
fprintf(fp, "Deleted ");
if (tb[NDA_LLADDR]) {
SPRINT_BUF(b1);
fprintf(fp, "%s ",
ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
RTA_PAYLOAD(tb[NDA_LLADDR]),
ll_index_to_type(r->ndm_ifindex),
b1, sizeof(b1)));
}
printf("%s\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t%s %s",
ll_index_to_name(r->ndm_ifindex),
addr[0], addr[1], addr[2],
addr[3], addr[4], addr[5],
state_n2a(r->ndm_state),
(r->ndm_flags & NTF_SELF) ? "self" : "master");
if (!filter_index && r->ndm_ifindex)
fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
if (tb[NDA_DST]) {
SPRINT_BUF(abuf);
fprintf(fp, "dst %s ",
format_host(AF_INET,
RTA_PAYLOAD(tb[NDA_DST]),
RTA_DATA(tb[NDA_DST]),
abuf, sizeof(abuf)));
}
if (show_stats && tb[NDA_CACHEINFO]) {
struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
int hz = get_user_hz();
printf("\t%8s", fmt_time(b1, sizeof(b1), ci->ndm_updated));
printf(" %8s", fmt_time(b1, sizeof(b1), ci->ndm_used));
fprintf(fp, " used %d/%d", ci->ndm_used/hz,
ci->ndm_updated/hz);
}
printf("\n");
if (r->ndm_flags & NTF_SELF)
fprintf(fp, "self ");
if (r->ndm_flags & NTF_MASTER)
fprintf(fp, "master ");
fprintf(fp, "%s\n", state_n2a(r->ndm_state));
return 0;
}
static int fdb_show(int argc, char **argv)
{
char *filter_dev = NULL;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
......@@ -128,8 +138,10 @@ static int fdb_show(int argc, char **argv)
}
if (filter_dev) {
if ((filter_index = if_nametoindex(filter_dev)) == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
filter_index = if_nametoindex(filter_dev);
if (filter_index == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n",
filter_dev);
return -1;
}
}
......@@ -138,11 +150,8 @@ static int fdb_show(int argc, char **argv)
perror("Cannot send dump request");
exit(1);
}
printf("port\tmac addr\t\tflags%s\n",
show_stats ? "\t updated used" : "");
if (rtnl_dump_filter(&rth, print_fdb, NULL) < 0) {
if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
......@@ -160,6 +169,8 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
char *addr = NULL;
char *d = NULL;
char abuf[ETH_ALEN];
int dst_ok = 0;
inet_prefix dst;
memset(&req, 0, sizeof(req));
......@@ -173,21 +184,27 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
d = *argv;
} else if (strcmp(*argv, "local") == 0) {
req.ndm.ndm_state = NUD_PERMANENT;
} else if (strcmp(*argv, "temp") == 0) {
req.ndm.ndm_state = NUD_REACHABLE;
} else if (strcmp(*argv, "dst") == 0) {
NEXT_ARG();
if (dst_ok)
duparg2("dst", *argv);
get_addr(&dst, *argv, preferred_family);
dst_ok = 1;
} else if (strcmp(*argv, "self") == 0) {
req.ndm.ndm_flags |= NTF_SELF;
} else if (strcmp(*argv, "master") == 0) {
} else if (matches(*argv, "master") == 0) {
req.ndm.ndm_flags |= NTF_MASTER;
} else if (matches(*argv, "local") == 0||
matches(*argv, "permanent") == 0) {
req.ndm.ndm_state |= NUD_PERMANENT;
} else if (matches(*argv, "temp") == 0) {
req.ndm.ndm_state |= NUD_REACHABLE;
} else {
if (strcmp(*argv, "to") == 0) {
NEXT_ARG();
}
if (matches(*argv, "help") == 0) {
NEXT_ARG();
}
if (matches(*argv, "help") == 0)
usage();
if (addr)
duparg2("to", *argv);
addr = *argv;
......@@ -200,7 +217,15 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
exit(-1);
}
if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
/* Assume self */
if (!(req.ndm.ndm_flags&(NTF_SELF|NTF_MASTER)))
req.ndm.ndm_flags |= NTF_SELF;
/* Assume permanent */
if (!(req.ndm.ndm_state&(NUD_PERMANENT|NUD_REACHABLE)))
req.ndm.ndm_state |= NUD_PERMANENT;
if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
abuf, abuf+1, abuf+2,
abuf+3, abuf+4, abuf+5) != 6) {
fprintf(stderr, "Invalid mac address %s\n", addr);
......@@ -208,6 +233,8 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
}
addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
if (dst_ok)
addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
req.ndm.ndm_ifindex = ll_name_to_index(d);
if (req.ndm.ndm_ifindex == 0) {
......
......@@ -3,7 +3,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o
iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
iplink_vxlan.o
RTMONOBJ=rtmon.o
......
......@@ -884,6 +884,7 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
lp = &linfo->head;
while ( (l = *lp) != NULL) {
int ok = 0;
int missing_net_address = 1;
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
struct nlmsg_list *a;
......@@ -891,8 +892,10 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
struct nlmsghdr *n = &a->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
if (ifa->ifa_index != ifi->ifi_index ||
(filter.family && filter.family != ifa->ifa_family))
if (ifa->ifa_index != ifi->ifi_index)
continue;
missing_net_address = 0;
if (filter.family && filter.family != ifa->ifa_family)
continue;
if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
continue;
......@@ -927,6 +930,9 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
ok = 1;
break;
}
if (missing_net_address &&
(filter.family == AF_UNSPEC || filter.family == AF_PACKET))
ok = 1;
if (!ok) {
*lp = l->next;
free(l);
......
/*
* iplink_vxlan.c VXLAN device support
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Stephen Hemminger <shemminger@vyatta.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <linux/ip.h>
#include <linux/if_link.h>
#include <arpa/inet.h>
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
static void explain(void)
{
fprintf(stderr, "Usage: ... vxlan id VNI [ group ADDR ] [ local ADDR ]\n");
fprintf(stderr, " [ ttl TTL ] [ tos TOS ] [ [no]learning ] [ dev PHYS_DEV ]\n");
fprintf(stderr, "\n");
fprintf(stderr, "Where: VNI := 0-16777215\n");
fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n");
fprintf(stderr, " TOS := { NUMBER | inherit }\n");
fprintf(stderr, " TTL := { 1..255 | inherit }\n");
}
static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
struct nlmsghdr *n)
{
__u32 vni = 0;
int vni_set = 0;
__u32 saddr = 0;
__u32 gaddr = 0;
unsigned link = 0;
__u8 tos = 0;
__u8 ttl = 0;
__u8 learning = 1;
__u8 noage = 0;
__u32 age = 0;
__u32 maxaddr = 0;
while (argc > 0) {
if (!matches(*argv, "id") ||
!matches(*argv, "vni")) {
NEXT_ARG();
if (get_u32(&vni, *argv, 0) ||
vni >= 1u << 24)
invarg("invalid id", *argv);
vni_set = 1;
} else if (!matches(*argv, "group")) {
NEXT_ARG();
gaddr = get_addr32(*argv);
if (!IN_MULTICAST(ntohl(gaddr)))
invarg("invald group address", *argv);
} else if (!matches(*argv, "local")) {
NEXT_ARG();
if (strcmp(*argv, "any"))
saddr = get_addr32(*argv);
if (IN_MULTICAST(ntohl(saddr)))
invarg("invalid local address", *argv);
} else if (!matches(*argv, "dev")) {
NEXT_ARG();
link = if_nametoindex(*argv);
if (link == 0)
exit(-1);
} else if (!matches(*argv, "ttl") ||
!matches(*argv, "hoplimit")) {
unsigned uval;
NEXT_ARG();
if (strcmp(*argv, "inherit") != 0) {
if (get_unsigned(&uval, *argv, 0))
invarg("invalid TTL\n", *argv);
if (uval > 255)
invarg("TTL must be <= 255\n", *argv);
ttl = uval;
}
} else if (!matches(*argv, "tos") ||
!matches(*argv, "dsfield")) {
__u32 uval;
NEXT_ARG();
if (strcmp(*argv, "inherit") != 0) {
if (rtnl_dsfield_a2n(&uval, *argv))
invarg("bad TOS value", *argv);
tos = uval;
} else
tos = 1;
} else if (!matches(*argv, "ageing")) {
NEXT_ARG();
if (strcmp(*argv, "none") == 0)
noage = 1;
else if (get_u32(&age, *argv, 0))
invarg("ageing timer\n", *argv);
} else if (!matches(*argv, "maxaddress")) {
NEXT_ARG();
if (strcmp(*argv, "unlimited") == 0)
maxaddr = 0;
else if (get_u32(&maxaddr, *argv, 0))
invarg("max addresses\n", *argv);
} else if (!matches(*argv, "nolearning")) {
learning = 0;
} else if (!matches(*argv, "learning")) {
learning = 1;
} else if (matches(*argv, "help") == 0) {
explain();
return -1;
} else {
fprintf(stderr, "vxlan: what is \"%s\"?\n", *argv);
explain();
return -1;
}
argc--, argv++;
}
if (!vni_set) {
fprintf(stderr, "vxlan: missing virtual network identifier\n");
return -1;
}
addattr32(n, 1024, IFLA_VXLAN_ID, vni);
addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4);
addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4);
if (link)
addattr32(n, 1024, IFLA_VXLAN_LINK, link);
addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
addattr8(n, 1024, IFLA_VXLAN_TOS, tos);
addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning);
if (noage)
addattr32(n, 1024, IFLA_VXLAN_AGEING, 0);
else if (age)
addattr32(n, 1024, IFLA_VXLAN_AGEING, age);
if (maxaddr)
addattr32(n, 1024, IFLA_VXLAN_LIMIT, maxaddr);
return 0;
}
static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
{
__u32 vni;
unsigned link;
char s1[1024];
char s2[64];
if (!tb)
return;
if (!tb[IFLA_VXLAN_ID] ||
RTA_PAYLOAD(tb[IFLA_VXLAN_ID]) < sizeof(__u32))
return;
vni = rta_getattr_u32(tb[IFLA_VXLAN_ID]);
fprintf(f, "id %u ", vni);
if (tb[IFLA_VXLAN_GROUP]) {
__u32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]);
if (addr)
fprintf(f, "group %s ",
format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
}
if (tb[IFLA_VXLAN_LOCAL]) {
unsigned addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]);
if (addr)
fprintf(f, "local %s ",
format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
}
if (tb[IFLA_VXLAN_LINK] &&
(link = rta_getattr_u32(tb[IFLA_VXLAN_LINK]))) {
const char *n = if_indextoname(link, s2);
if (n)
fprintf(f, "dev %s ", n);
else
fprintf(f, "dev %u ", link);
}
if (tb[IFLA_VXLAN_LEARNING] &&
!rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]))
fputs("nolearning ", f);
if (tb[IFLA_VXLAN_TOS]) {
__u8 tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]);
if (tos == 1)
fprintf(f, "tos inherit ");
else
fprintf(f, "tos %#x ", tos);
}
if (tb[IFLA_VXLAN_TTL]) {
__u8 ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]);
if (ttl)
fprintf(f, "ttl %d ", ttl);
}
if (tb[IFLA_VXLAN_AGEING]) {
__u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]);
if (age == 0)
fprintf(f, "ageing none ");
else
fprintf(f, "ageing %u ", age);
}
if (tb[IFLA_VXLAN_LIMIT]) {
__u32 maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT]);
if (maxaddr == 0)
fprintf(f, "maxaddr unlimited ");
else
fprintf(f, "maxaddr %u ", maxaddr);
}
}
struct link_util vxlan_link_util = {
.id = "vxlan",
.maxattr = IFLA_VXLAN_MAX,
.parse_opt = vxlan_parse_opt,
.print_opt = vxlan_print_opt,
};
......@@ -311,9 +311,9 @@ those headers.
.PP
.nf
site-A:# ip link set l2tpeth0 up mtu 1446
site-A:# brctl addbr br0
site-A:# brctl addif br0 l2tpeth0
site-A:# brctl addif br0 eth0
site-A:# ip link add br0 type bridge
site-A:# ip link set l2tpeth0 master br0
site-A:# ip link set eth0 master br0
site-A:# ip link set br0 up
.fi
.PP
......@@ -323,9 +323,9 @@ over an L2TP pseudowire:
.PP
.nf
site-A:# ip link set l2tpeth0 up mtu 1446
site-A:# brctl addbr brvlan5
site-A:# brctl addif brvlan5 l2tpeth0.5
site-A:# brctl addif brvlan5 eth1.5
site-A:# ip link add brvlan5 type bridge
site-A:# ip link set l2tpeth0.5 master brvlan5
site-A:# ip link set eth1.5 master brvlan5
site-A:# ip link set brvlan5 up
.fi
.PP
......@@ -370,7 +370,6 @@ dynamic tunnels. If a non-Linux peer supports Hello messages in
unmanaged tunnels, it must be turned off to interoperate with Linux.
.SH SEE ALSO
.br
.BR brctl (8)
.BR ip (8)
.SH AUTHOR
James Chapman <jchapman@katalix.com>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment