Blame view
net/8021q/vlan_netlink.c
6.52 KB
07b5b17e1
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* * VLAN netlink control interface * * Copyright (c) 2007 Patrick McHardy <kaber@trash.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/if_vlan.h> |
3a9a231d9
|
14 |
#include <linux/module.h> |
881d966b4
|
15 |
#include <net/net_namespace.h> |
07b5b17e1
|
16 17 18 19 20 21 22 23 24 25 |
#include <net/netlink.h> #include <net/rtnetlink.h> #include "vlan.h" static const struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = { [IFLA_VLAN_ID] = { .type = NLA_U16 }, [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, |
8ad227ff8
|
26 |
[IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, |
07b5b17e1
|
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
}; static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = { [IFLA_VLAN_QOS_MAPPING] = { .len = sizeof(struct ifla_vlan_qos_mapping) }, }; static inline int vlan_validate_qos_map(struct nlattr *attr) { if (!attr) return 0; return nla_validate_nested(attr, IFLA_VLAN_QOS_MAX, vlan_map_policy); } static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) { struct ifla_vlan_flags *flags; u16 id; int err; |
0e06877c6
|
46 47 48 49 50 51 |
if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) return -EINVAL; if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } |
07b5b17e1
|
52 53 |
if (!data) return -EINVAL; |
8ad227ff8
|
54 55 |
if (data[IFLA_VLAN_PROTOCOL]) { switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) { |
f0e78826e
|
56 57 |
case htons(ETH_P_8021Q): case htons(ETH_P_8021AD): |
8ad227ff8
|
58 59 60 61 62 |
break; default: return -EPROTONOSUPPORT; } } |
07b5b17e1
|
63 64 65 66 67 68 69 |
if (data[IFLA_VLAN_ID]) { id = nla_get_u16(data[IFLA_VLAN_ID]); if (id >= VLAN_VID_MASK) return -ERANGE; } if (data[IFLA_VLAN_FLAGS]) { flags = nla_data(data[IFLA_VLAN_FLAGS]); |
70c03b49b
|
70 |
if ((flags->flags & flags->mask) & |
5e7565930
|
71 |
~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | |
86fbe9bb5
|
72 |
VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) |
07b5b17e1
|
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
return -EINVAL; } err = vlan_validate_qos_map(data[IFLA_VLAN_INGRESS_QOS]); if (err < 0) return err; err = vlan_validate_qos_map(data[IFLA_VLAN_EGRESS_QOS]); if (err < 0) return err; return 0; } static int vlan_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { |
07b5b17e1
|
88 89 90 91 92 93 94 |
struct ifla_vlan_flags *flags; struct ifla_vlan_qos_mapping *m; struct nlattr *attr; int rem; if (data[IFLA_VLAN_FLAGS]) { flags = nla_data(data[IFLA_VLAN_FLAGS]); |
b3ce0325f
|
95 |
vlan_dev_change_flags(dev, flags->flags, flags->mask); |
07b5b17e1
|
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
} if (data[IFLA_VLAN_INGRESS_QOS]) { nla_for_each_nested(attr, data[IFLA_VLAN_INGRESS_QOS], rem) { m = nla_data(attr); vlan_dev_set_ingress_priority(dev, m->to, m->from); } } if (data[IFLA_VLAN_EGRESS_QOS]) { nla_for_each_nested(attr, data[IFLA_VLAN_EGRESS_QOS], rem) { m = nla_data(attr); vlan_dev_set_egress_priority(dev, m->from, m->to); } } return 0; } |
81adee47d
|
111 |
static int vlan_newlink(struct net *src_net, struct net_device *dev, |
07b5b17e1
|
112 113 |
struct nlattr *tb[], struct nlattr *data[]) { |
7da82c06d
|
114 |
struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
07b5b17e1
|
115 |
struct net_device *real_dev; |
8ad227ff8
|
116 |
__be16 proto; |
07b5b17e1
|
117 118 119 120 121 122 123 |
int err; if (!data[IFLA_VLAN_ID]) return -EINVAL; if (!tb[IFLA_LINK]) return -EINVAL; |
81adee47d
|
124 |
real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); |
07b5b17e1
|
125 126 |
if (!real_dev) return -ENODEV; |
8ad227ff8
|
127 128 129 130 131 132 |
if (data[IFLA_VLAN_PROTOCOL]) proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]); else proto = htons(ETH_P_8021Q); vlan->vlan_proto = proto; |
1fd9b1fc3
|
133 134 135 |
vlan->vlan_id = nla_get_u16(data[IFLA_VLAN_ID]); vlan->real_dev = real_dev; vlan->flags = VLAN_FLAG_REORDER_HDR; |
07b5b17e1
|
136 |
|
1fd9b1fc3
|
137 |
err = vlan_check_real_dev(real_dev, vlan->vlan_proto, vlan->vlan_id); |
07b5b17e1
|
138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
if (err < 0) return err; if (!tb[IFLA_MTU]) dev->mtu = real_dev->mtu; else if (dev->mtu > real_dev->mtu) return -EINVAL; err = vlan_changelink(dev, tb, data); if (err < 0) return err; return register_vlan_dev(dev); } |
07b5b17e1
|
152 153 154 155 156 157 158 159 160 161 162 |
static inline size_t vlan_qos_map_size(unsigned int n) { if (n == 0) return 0; /* IFLA_VLAN_{EGRESS,INGRESS}_QOS + n * IFLA_VLAN_QOS_MAPPING */ return nla_total_size(sizeof(struct nlattr)) + nla_total_size(sizeof(struct ifla_vlan_qos_mapping)) * n; } static size_t vlan_get_size(const struct net_device *dev) { |
7da82c06d
|
163 |
struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
07b5b17e1
|
164 |
|
8ad227ff8
|
165 166 |
return nla_total_size(2) + /* IFLA_VLAN_PROTOCOL */ nla_total_size(2) + /* IFLA_VLAN_ID */ |
c33a39c57
|
167 |
nla_total_size(sizeof(struct ifla_vlan_flags)) + /* IFLA_VLAN_FLAGS */ |
07b5b17e1
|
168 169 170 171 172 173 |
vlan_qos_map_size(vlan->nr_ingress_mappings) + vlan_qos_map_size(vlan->nr_egress_mappings); } static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) { |
7da82c06d
|
174 |
struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
07b5b17e1
|
175 176 177 178 179 |
struct vlan_priority_tci_mapping *pm; struct ifla_vlan_flags f; struct ifla_vlan_qos_mapping m; struct nlattr *nest; unsigned int i; |
8ad227ff8
|
180 181 |
if (nla_put_be16(skb, IFLA_VLAN_PROTOCOL, vlan->vlan_proto) || nla_put_u16(skb, IFLA_VLAN_ID, vlan->vlan_id)) |
61849dda1
|
182 |
goto nla_put_failure; |
07b5b17e1
|
183 184 185 |
if (vlan->flags) { f.flags = vlan->flags; f.mask = ~0; |
61849dda1
|
186 187 |
if (nla_put(skb, IFLA_VLAN_FLAGS, sizeof(f), &f)) goto nla_put_failure; |
07b5b17e1
|
188 189 190 191 192 193 194 195 196 197 198 199 |
} if (vlan->nr_ingress_mappings) { nest = nla_nest_start(skb, IFLA_VLAN_INGRESS_QOS); if (nest == NULL) goto nla_put_failure; for (i = 0; i < ARRAY_SIZE(vlan->ingress_priority_map); i++) { if (!vlan->ingress_priority_map[i]) continue; m.from = i; m.to = vlan->ingress_priority_map[i]; |
61849dda1
|
200 201 202 |
if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, sizeof(m), &m)) goto nla_put_failure; |
07b5b17e1
|
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
} nla_nest_end(skb, nest); } if (vlan->nr_egress_mappings) { nest = nla_nest_start(skb, IFLA_VLAN_EGRESS_QOS); if (nest == NULL) goto nla_put_failure; for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { for (pm = vlan->egress_priority_map[i]; pm; pm = pm->next) { if (!pm->vlan_qos) continue; m.from = pm->priority; m.to = (pm->vlan_qos >> 13) & 0x7; |
61849dda1
|
220 221 222 |
if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, sizeof(m), &m)) goto nla_put_failure; |
07b5b17e1
|
223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
} } nla_nest_end(skb, nest); } return 0; nla_put_failure: return -EMSGSIZE; } struct rtnl_link_ops vlan_link_ops __read_mostly = { .kind = "vlan", .maxtype = IFLA_VLAN_MAX, .policy = vlan_policy, |
7da82c06d
|
237 |
.priv_size = sizeof(struct vlan_dev_priv), |
07b5b17e1
|
238 239 240 241 |
.setup = vlan_setup, .validate = vlan_validate, .newlink = vlan_newlink, .changelink = vlan_changelink, |
af3015170
|
242 |
.dellink = unregister_vlan_dev, |
07b5b17e1
|
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
.get_size = vlan_get_size, .fill_info = vlan_fill_info, }; int __init vlan_netlink_init(void) { return rtnl_link_register(&vlan_link_ops); } void __exit vlan_netlink_fini(void) { rtnl_link_unregister(&vlan_link_ops); } MODULE_ALIAS_RTNL_LINK("vlan"); |