15c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy/*
25c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy * iplink_vlan.c	VLAN device support
35c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy *
45c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy *              This program is free software; you can redistribute it and/or
55c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy *              modify it under the terms of the GNU General Public License
65c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy *              as published by the Free Software Foundation; either version
75c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy *              2 of the License, or (at your option) any later version.
85c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy *
95c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy * Authors:     Patrick McHardy <kaber@trash.net>
105c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy */
115c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
125c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include <stdio.h>
135c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include <stdlib.h>
145c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include <string.h>
155c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include <linux/if_vlan.h>
165c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
175c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include "rt_names.h"
185c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include "utils.h"
195c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#include "ip_common.h"
205c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
215c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic void explain(void)
225c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
235c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	fprintf(stderr,
245c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"Usage: ... vlan id VLANID [ FLAG-LIST ]\n"
255c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"                          [ ingress-qos-map QOS-MAP ] [ egress-qos-map QOS-MAP ]\n"
265c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"\n"
275c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"VLANID := 0-4095\n"
285c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
2947420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy		"FLAG := [ reorder_hdr { on | off } ] [ gvrp { on | off } ]\n"
302180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy		"        [ loose_binding { on | off } ]\n"
315c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"QOS-MAP := [ QOS-MAP ] QOS-MAPPING\n"
325c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		"QOS-MAPPING := FROM:TO\n"
335c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	);
345c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
355c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
365c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic int on_off(char *msg)
375c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
385c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
395c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	return -1;
405c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
415c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
425c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n,
435c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			      int attrtype)
445c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
455c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	int argc = *argcp;
465c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	char **argv = *argvp;
475c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	struct ifla_vlan_qos_mapping m;
485c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	struct rtattr *tail;
495c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
505c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	tail = NLMSG_TAIL(n);
515c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	addattr_l(n, 1024, attrtype, NULL, 0);
525c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
535c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	while (argc > 0) {
545c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		char *colon = strchr(*argv, ':');
555c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
565c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		if (!colon)
575c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			break;
585c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		*colon = '\0';
595c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
605c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		if (get_u32(&m.from, *argv, 0))
615c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			return 1;
625c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		if (get_u32(&m.to, colon + 1, 0))
635c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			return 1;
645c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		argc--, argv++;
655c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
665c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m));
675c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	}
685c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
695c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *)tail;
705c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
715c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	*argcp = argc;
725c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	*argvp = argv;
735c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	return 0;
745c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
755c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
765c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic int vlan_parse_opt(struct link_util *lu, int argc, char **argv,
775c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			  struct nlmsghdr *n)
785c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
795c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	struct ifla_vlan_flags flags = { 0 };
805c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	__u16 id;
815c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
825c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	while (argc > 0) {
835c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		if (matches(*argv, "id") == 0) {
845c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			NEXT_ARG();
855c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			if (get_u16(&id, *argv, 0))
865c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy				invarg("id is invalid", *argv);
875c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			addattr_l(n, 1024, IFLA_VLAN_ID, &id, 2);
885c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		} else if (matches(*argv, "reorder_hdr") == 0) {
895c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			NEXT_ARG();
905c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			flags.mask |= VLAN_FLAG_REORDER_HDR;
915c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			if (strcmp(*argv, "on") == 0)
925c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy				flags.flags |= VLAN_FLAG_REORDER_HDR;
935c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			else if (strcmp(*argv, "off") == 0)
945c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy				flags.flags &= ~VLAN_FLAG_REORDER_HDR;
955c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			else
965c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy				return on_off("reorder_hdr");
9747420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy		} else if (matches(*argv, "gvrp") == 0) {
9847420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy			NEXT_ARG();
9947420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy			flags.mask |= VLAN_FLAG_GVRP;
10047420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy			if (strcmp(*argv, "on") == 0)
10147420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy				flags.flags |= VLAN_FLAG_GVRP;
10247420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy			else if (strcmp(*argv, "off") == 0)
10347420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy				flags.flags &= ~VLAN_FLAG_GVRP;
10447420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy			else
10547420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy				return on_off("gvrp");
1062180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy		} else if (matches(*argv, "loose_binding") == 0) {
1072180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy			NEXT_ARG();
1082180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy			flags.mask |= VLAN_FLAG_LOOSE_BINDING;
1092180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy			if (strcmp(*argv, "on") == 0)
1102180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy				flags.flags |= VLAN_FLAG_LOOSE_BINDING;
1112180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy			else if (strcmp(*argv, "off") == 0)
1122180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy				flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
1132180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy			else
1142180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy				return on_off("loose_binding");
1155c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		} else if (matches(*argv, "ingress-qos-map") == 0) {
1165c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			NEXT_ARG();
1175c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			if (vlan_parse_qos_map(&argc, &argv, n,
1185c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy					       IFLA_VLAN_INGRESS_QOS))
1195c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy				invarg("invalid ingress-qos-map", *argv);
1205c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			continue;
1215c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		} else if (matches(*argv, "egress-qos-map") == 0) {
1225c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			NEXT_ARG();
1235c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			if (vlan_parse_qos_map(&argc, &argv, n,
1245c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy					       IFLA_VLAN_EGRESS_QOS))
1255c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy				invarg("invalid egress-qos-map", *argv);
1265c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			continue;
1275c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		} else if (matches(*argv, "help") == 0) {
1285c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			explain();
1295c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			return -1;
1305c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		} else {
1315c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			fprintf(stderr, "vlan: what is \"%s\"?\n", *argv);
1325c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			explain();
1335c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			return -1;
1345c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		}
1355c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		argc--, argv++;
1365c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	}
1375c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1385c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (flags.mask)
1395c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		addattr_l(n, 1024, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
1405c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1415c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	return 0;
1425c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
1435c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1445c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic void vlan_print_map(FILE *f, char *name, struct rtattr *attr)
1455c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
1465c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	struct ifla_vlan_qos_mapping *m;
1475c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	struct rtattr *i;
1485c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	int rem;
1495c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1505c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	fprintf(f, "\n      %s { ", name);
1515c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1525c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	rem = RTA_PAYLOAD(attr);
1535c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
1545c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		m = RTA_DATA(i);
1555c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		fprintf(f, "%u:%u ", m->from, m->to);
1565c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	}
1575c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	fprintf(f, "} ");
1585c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
1595c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1605c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic void vlan_print_flags(FILE *fp, __u32 flags)
1615c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
1625c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	fprintf(fp, "<");
1635c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#define _PF(f)	if (flags & VLAN_FLAG_##f) { \
1645c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			flags &= ~ VLAN_FLAG_##f; \
1655c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			fprintf(fp, #f "%s", flags ? "," : ""); \
1665c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		}
1675c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	_PF(REORDER_HDR);
16847420640687cbd389ddd99d39cf7fb0e0bcb265aPatrick McHardy	_PF(GVRP);
1692180b6b50bdc50e1a7740e5283930088da0bbae7Patrick McHardy	_PF(LOOSE_BINDING);
1705c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy#undef _PF
1715c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (flags)
1725c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		fprintf(fp, "%x", flags);
1735c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	fprintf(fp, "> ");
1745c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
1755c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1765c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystatic void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
1775c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy{
1785c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	struct ifla_vlan_flags *flags;
1795c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (!tb)
1805c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		return;
1815c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1825c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (!tb[IFLA_VLAN_ID] ||
1835c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	    RTA_PAYLOAD(tb[IFLA_VLAN_ID]) < sizeof(__u16))
1845c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		return;
1855c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
186ff24746cca1ef0c92d46614158e6672acd6b63d3Stephen Hemminger	fprintf(f, "id %u ", rta_getattr_u16(tb[IFLA_VLAN_ID]));
1875c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
1885c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (tb[IFLA_VLAN_FLAGS]) {
1895c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags))
1905c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy			return;
1915c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		flags = RTA_DATA(tb[IFLA_VLAN_FLAGS]);
1925c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		vlan_print_flags(f, flags->flags);
1935c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	}
1945c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (tb[IFLA_VLAN_INGRESS_QOS])
1955c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		vlan_print_map(f, "ingress-qos-map", tb[IFLA_VLAN_INGRESS_QOS]);
1965c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	if (tb[IFLA_VLAN_EGRESS_QOS])
1975c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy		vlan_print_map(f, "egress-qos-map", tb[IFLA_VLAN_EGRESS_QOS]);
1985c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy}
1995c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy
2005c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardystruct link_util vlan_link_util = {
2015c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	.id		= "vlan",
2025c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	.maxattr	= IFLA_VLAN_MAX,
2035c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	.parse_opt	= vlan_parse_opt,
2045c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy	.print_opt	= vlan_print_opt,
2055c302d518f10e67ddab9e44207a0c878214ed389Patrick McHardy};
206