static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
{
struct ifla_vf_mac *vf_mac;
- struct ifla_vf_vlan *vf_vlan;
struct ifla_vf_tx_rate *vf_tx_rate;
struct ifla_vf_spoofchk *vf_spoofchk;
struct ifla_vf_link_state *vf_linkstate;
parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
- vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
/* Check if the spoof checking vf info type is supported by
fprintf(fp, "%s vf %d MAC %s", _SL_, vf_mac->vf,
ll_addr_n2a((unsigned char *)&vf_mac->mac,
ETH_ALEN, 0, b1, sizeof(b1)));
- if (vf_vlan->vlan)
- fprintf(fp, ", vlan %d", vf_vlan->vlan);
- if (vf_vlan->qos)
- fprintf(fp, ", qos %d", vf_vlan->qos);
+ if (vf[IFLA_VF_VLAN_LIST]) {
+ struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST];
+ int rem = RTA_PAYLOAD(vfvlanlist);
+
+ for (i = RTA_DATA(vfvlanlist);
+ RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+ struct ifla_vf_vlan_info *vf_vlan_info =
+ RTA_DATA(i);
+ SPRINT_BUF(b2);
+
+ if (vf_vlan_info->vlan)
+ fprintf(fp, ", vlan %d", vf_vlan_info->vlan);
+ if (vf_vlan_info->qos)
+ fprintf(fp, ", qos %d", vf_vlan_info->qos);
+ if (vf_vlan_info->vlan_proto &&
+ vf_vlan_info->vlan_proto != htons(ETH_P_8021Q))
+ fprintf(fp, ", vlan protocol %s",
+ ll_proto_n2a(vf_vlan_info->vlan_proto,
+ b2, sizeof(b2)));
+
+ }
+ } else {
+ struct ifla_vf_vlan *vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
+
+ if (vf_vlan->vlan)
+ fprintf(fp, ", vlan %d", vf_vlan->vlan);
+ if (vf_vlan->qos)
+ fprintf(fp, ", qos %d", vf_vlan->qos);
+ }
if (vf_tx_rate->rate)
fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
fprintf(stderr, " [ link-netnsid ID ]\n");
fprintf(stderr, " [ alias NAME ]\n");
fprintf(stderr, " [ vf NUM [ mac LLADDR ]\n");
- fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] ]\n");
+ fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n");
fprintf(stderr, " [ rate TXRATE ]\n");
fprintf(stderr, " [ max_tx_rate TXRATE ]\n");
return RTA_PAYLOAD(tb[IFLA_ADDRESS]);
}
+static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
+ struct ifla_vf_vlan_info *ivvip)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ NEXT_ARG();
+ if (get_unsigned(&ivvip->vlan, *argv, 0))
+ invarg("Invalid \"vlan\" value\n", *argv);
+
+ ivvip->vf = vf;
+ ivvip->qos = 0;
+ ivvip->vlan_proto = htons(ETH_P_8021Q);
+ if (NEXT_ARG_OK()) {
+ NEXT_ARG();
+ if (matches(*argv, "qos") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&ivvip->qos, *argv, 0))
+ invarg("Invalid \"qos\" value\n", *argv);
+ } else {
+ /* rewind arg */
+ PREV_ARG();
+ }
+ }
+ if (NEXT_ARG_OK()) {
+ NEXT_ARG();
+ if (matches(*argv, "proto") == 0) {
+ NEXT_ARG();
+ if (ll_proto_a2n(&ivvip->vlan_proto, *argv))
+ invarg("protocol is invalid\n", *argv);
+ if (ivvip->vlan_proto != htons(ETH_P_8021AD) &&
+ ivvip->vlan_proto != htons(ETH_P_8021Q)) {
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+ char msg[64 + sizeof(b1) + sizeof(b2)];
+
+ sprintf(msg, "Invalid \"vlan protocol\""
+ " value - supported %s, %s\n",
+ ll_proto_n2a(htons(ETH_P_8021Q),
+ b1, sizeof(b1)),
+ ll_proto_n2a(htons(ETH_P_8021AD),
+ b2, sizeof(b2)));
+ invarg(msg, *argv);
+ }
+ } else {
+ /* rewind arg */
+ PREV_ARG();
+ }
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+}
+
static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
struct iplink_req *req, int dev_index)
{
addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC,
&ivm, sizeof(ivm));
} else if (matches(*argv, "vlan") == 0) {
- struct ifla_vf_vlan ivv;
+ struct ifla_vf_vlan_info ivvi;
- NEXT_ARG();
- if (get_unsigned(&ivv.vlan, *argv, 0))
- invarg("Invalid \"vlan\" value\n", *argv);
+ iplink_parse_vf_vlan_info(vf, &argc, &argv, &ivvi);
+ /* support the old interface in case of older kernel*/
+ if (ivvi.vlan_proto == htons(ETH_P_8021Q)) {
+ struct ifla_vf_vlan ivv;
- ivv.vf = vf;
- ivv.qos = 0;
- if (NEXT_ARG_OK()) {
- NEXT_ARG();
- if (matches(*argv, "qos") == 0) {
+ ivv.vf = ivvi.vf;
+ ivv.vlan = ivvi.vlan;
+ ivv.qos = ivvi.qos;
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_VF_VLAN, &ivv, sizeof(ivv));
+ } else {
+ struct rtattr *vfvlanlist;
+
+ vfvlanlist = addattr_nest(&req->n, sizeof(*req),
+ IFLA_VF_VLAN_LIST);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_VF_VLAN_INFO, &ivvi,
+ sizeof(ivvi));
+
+ while (NEXT_ARG_OK()) {
NEXT_ARG();
- if (get_unsigned(&ivv.qos, *argv, 0))
- invarg("Invalid \"qos\" value\n", *argv);
- } else {
- /* rewind arg */
- PREV_ARG();
+ if (matches(*argv, "vlan") != 0) {
+ PREV_ARG();
+ break;
+ }
+ iplink_parse_vf_vlan_info(vf, &argc,
+ &argv, &ivvi);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_VF_VLAN_INFO, &ivvi,
+ sizeof(ivvi));
}
+ addattr_nest_end(&req->n, vfvlanlist);
}
- addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN,
- &ivv, sizeof(ivv));
} else if (matches(*argv, "rate") == 0) {
struct ifla_vf_tx_rate ivt;
.IR LLADDR " ]"
.br
.in +9
-.RB "[ " vlan
-.IR VLANID " [ "
-.B qos
-.IR VLAN-QOS " ] ]"
+.RI "[ " VFVLAN-LIST " ]"
.br
.RB "[ " rate
.IR TXRATE " ]"
.IR ETYPE " := [ " TYPE " |"
.BR bridge_slave " | " bond_slave " ]"
+.ti -8
+.IR VFVLAN-LIST " := [ " VFVLAN-LIST " ] " VFVLAN
+
+.ti -8
+.IR VFVLAN " := "
+.RB "[ " vlan
+.IR VLANID " [ "
+.B qos
+.IR VLAN-QOS " ] ["
+.B proto
+.IR VLAN-PROTO " ] ]"
+
.SH "DESCRIPTION"
.SS ip link add - add virtual link
.B qos
as 0 disables VLAN tagging and filtering for the VF.
+.sp
+.BI proto " VLAN-PROTO"
+- assign VLAN PROTOCOL for the VLAN tag, either 802.1Q or 802.1ad.
+Setting to 802.1ad, all traffic sent from the VF will be tagged with VLAN S-Tag.
+Incoming traffic will have VLAN S-Tags stripped before being passed to the VF.
+Setting to 802.1ad also enables an option to concatenate another VLAN tag, so both
+S-TAG and C-TAG will be inserted/stripped for outgoing/incoming traffic, respectively.
+If not specified, the value is assumed to be 802.1Q. Both the
+.B vf
+and
+.B vlan
+parameters must be specified.
+
.sp
.BI rate " TXRATE"
-- change the allowed transmit bandwidth, in Mbps, for the specified VF.