00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <netlink-local.h>
00021 #include <netlink/netlink.h>
00022 #include <netlink/attr.h>
00023 #include <netlink/utils.h>
00024 #include <netlink/object.h>
00025 #include <netlink/route/rtnl.h>
00026 #include <netlink/route/link/api.h>
00027 #include <netlink/route/link/vlan.h>
00028
00029 #include <linux/if_vlan.h>
00030
00031
00032 #define VLAN_HAS_ID (1<<0)
00033 #define VLAN_HAS_FLAGS (1<<1)
00034 #define VLAN_HAS_INGRESS_QOS (1<<2)
00035 #define VLAN_HAS_EGRESS_QOS (1<<3)
00036
00037 struct vlan_info
00038 {
00039 uint16_t vi_vlan_id;
00040 uint32_t vi_flags;
00041 uint32_t vi_flags_mask;
00042 uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1];
00043 uint32_t vi_negress;
00044 uint32_t vi_egress_size;
00045 struct vlan_map * vi_egress_qos;
00046 uint32_t vi_mask;
00047 };
00048
00049
00050 static const struct trans_tbl vlan_flags[] = {
00051 __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
00052 };
00053
00054 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
00055 {
00056 return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
00057 }
00058
00059 int rtnl_link_vlan_str2flags(const char *name)
00060 {
00061 return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
00062 }
00063
00064 static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
00065 [IFLA_VLAN_ID] = { .type = NLA_U16 },
00066 [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) },
00067 [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
00068 [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED },
00069 };
00070
00071 static int vlan_alloc(struct rtnl_link *link)
00072 {
00073 struct vlan_info *vi;
00074
00075 if ((vi = calloc(1, sizeof(*vi))) == NULL)
00076 return -NLE_NOMEM;
00077
00078 link->l_info = vi;
00079
00080 return 0;
00081 }
00082
00083 static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
00084 struct nlattr *xstats)
00085 {
00086 struct nlattr *tb[IFLA_VLAN_MAX+1];
00087 struct vlan_info *vi;
00088 int err;
00089
00090 NL_DBG(3, "Parsing VLAN link info");
00091
00092 if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
00093 goto errout;
00094
00095 if ((err = vlan_alloc(link)) < 0)
00096 goto errout;
00097
00098 vi = link->l_info;
00099
00100 if (tb[IFLA_VLAN_ID]) {
00101 vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]);
00102 vi->vi_mask |= VLAN_HAS_ID;
00103 }
00104
00105 if (tb[IFLA_VLAN_FLAGS]) {
00106 struct ifla_vlan_flags flags;
00107 nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
00108
00109 vi->vi_flags = flags.flags;
00110 vi->vi_mask |= VLAN_HAS_FLAGS;
00111 }
00112
00113 if (tb[IFLA_VLAN_INGRESS_QOS]) {
00114 struct ifla_vlan_qos_mapping *map;
00115 struct nlattr *nla;
00116 int remaining;
00117
00118 memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos));
00119
00120 nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
00121 if (nla_len(nla) < sizeof(*map))
00122 return -NLE_INVAL;
00123
00124 map = nla_data(nla);
00125 if (map->from < 0 || map->from > VLAN_PRIO_MAX) {
00126 return -NLE_INVAL;
00127 }
00128
00129 vi->vi_ingress_qos[map->from] = map->to;
00130 }
00131
00132 vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
00133 }
00134
00135 if (tb[IFLA_VLAN_EGRESS_QOS]) {
00136 struct ifla_vlan_qos_mapping *map;
00137 struct nlattr *nla;
00138 int remaining, i = 0;
00139
00140 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
00141 if (nla_len(nla) < sizeof(*map))
00142 return -NLE_INVAL;
00143 i++;
00144 }
00145
00146
00147 vi->vi_egress_size = (i + 32) & ~31;
00148 vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
00149 if (vi->vi_egress_qos == NULL)
00150 return -NLE_NOMEM;
00151
00152 i = 0;
00153 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
00154 map = nla_data(nla);
00155 NL_DBG(4, "Assigning egress qos mapping %d\n", i);
00156 vi->vi_egress_qos[i].vm_from = map->from;
00157 vi->vi_egress_qos[i++].vm_to = map->to;
00158 }
00159
00160 vi->vi_negress = i;
00161 vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
00162 }
00163
00164 err = 0;
00165 errout:
00166 return err;
00167 }
00168
00169 static void vlan_free(struct rtnl_link *link)
00170 {
00171 struct vlan_info *vi = link->l_info;
00172
00173 if (vi) {
00174 free(vi->vi_egress_qos);
00175 vi->vi_egress_qos = NULL;
00176 }
00177
00178 free(vi);
00179 link->l_info = NULL;
00180 }
00181
00182 static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
00183 {
00184 struct vlan_info *vi = link->l_info;
00185
00186 nl_dump(p, "vlan-id %d", vi->vi_vlan_id);
00187 }
00188
00189 static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
00190 {
00191 struct vlan_info *vi = link->l_info;
00192 int i, printed;
00193 char buf[64];
00194
00195 rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
00196 nl_dump_line(p, " vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
00197
00198 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
00199 nl_dump_line(p,
00200 " ingress vlan prio -> qos/socket prio mapping:\n");
00201 for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
00202 if (vi->vi_ingress_qos[i]) {
00203 if (printed == 0)
00204 nl_dump_line(p, " ");
00205 nl_dump(p, "%x -> %#08x, ",
00206 i, vi->vi_ingress_qos[i]);
00207 if (printed++ == 3) {
00208 nl_dump(p, "\n");
00209 printed = 0;
00210 }
00211 }
00212 }
00213
00214 if (printed > 0 && printed != 4)
00215 nl_dump(p, "\n");
00216 }
00217
00218 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
00219 nl_dump_line(p,
00220 " egress qos/socket prio -> vlan prio mapping:\n");
00221 for (i = 0, printed = 0; i < vi->vi_negress; i++) {
00222 if (printed == 0)
00223 nl_dump_line(p, " ");
00224 nl_dump(p, "%#08x -> %x, ",
00225 vi->vi_egress_qos[i].vm_from,
00226 vi->vi_egress_qos[i].vm_to);
00227 if (printed++ == 3) {
00228 nl_dump(p, "\n");
00229 printed = 0;
00230 }
00231 }
00232
00233 if (printed > 0 && printed != 4)
00234 nl_dump(p, "\n");
00235 }
00236 }
00237
00238 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
00239 {
00240 struct vlan_info *vdst, *vsrc = src->l_info;
00241 int err;
00242
00243 dst->l_info = NULL;
00244 if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0)
00245 return err;
00246 vdst = dst->l_info;
00247
00248 vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
00249 sizeof(struct vlan_map));
00250 if (!vdst->vi_egress_qos)
00251 return -NLE_NOMEM;
00252
00253 memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
00254 vsrc->vi_egress_size * sizeof(struct vlan_map));
00255
00256 return 0;
00257 }
00258
00259 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
00260 {
00261 struct vlan_info *vi = link->l_info;
00262 struct nlattr *data;
00263
00264 if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
00265 return -NLE_MSGSIZE;
00266
00267 if (vi->vi_mask & VLAN_HAS_ID)
00268 NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
00269
00270 if (vi->vi_mask & VLAN_HAS_FLAGS) {
00271 struct ifla_vlan_flags flags = {
00272 .flags = vi->vi_flags,
00273 .mask = vi->vi_flags_mask,
00274 };
00275
00276 NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
00277 }
00278
00279 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
00280 struct ifla_vlan_qos_mapping map;
00281 struct nlattr *qos;
00282 int i;
00283
00284 if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
00285 goto nla_put_failure;
00286
00287 for (i = 0; i <= VLAN_PRIO_MAX; i++) {
00288 if (vi->vi_ingress_qos[i]) {
00289 map.from = i;
00290 map.to = vi->vi_ingress_qos[i];
00291
00292 NLA_PUT(msg, i, sizeof(map), &map);
00293 }
00294 }
00295
00296 nla_nest_end(msg, qos);
00297 }
00298
00299 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
00300 struct ifla_vlan_qos_mapping map;
00301 struct nlattr *qos;
00302 int i;
00303
00304 if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
00305 goto nla_put_failure;
00306
00307 for (i = 0; i < vi->vi_negress; i++) {
00308 map.from = vi->vi_egress_qos[i].vm_from;
00309 map.to = vi->vi_egress_qos[i].vm_to;
00310
00311 NLA_PUT(msg, i, sizeof(map), &map);
00312 }
00313
00314 nla_nest_end(msg, qos);
00315 }
00316
00317 nla_nest_end(msg, data);
00318
00319 nla_put_failure:
00320
00321 return 0;
00322 }
00323
00324 static struct rtnl_link_info_ops vlan_info_ops = {
00325 .io_name = "vlan",
00326 .io_alloc = vlan_alloc,
00327 .io_parse = vlan_parse,
00328 .io_dump = {
00329 [NL_DUMP_LINE] = vlan_dump_line,
00330 [NL_DUMP_DETAILS] = vlan_dump_details,
00331 },
00332 .io_clone = vlan_clone,
00333 .io_put_attrs = vlan_put_attrs,
00334 .io_free = vlan_free,
00335 };
00336
00337 int rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
00338 {
00339 struct vlan_info *vi = link->l_info;
00340
00341 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00342 return -NLE_OPNOTSUPP;
00343
00344 vi->vi_vlan_id = id;
00345 vi->vi_mask |= VLAN_HAS_ID;
00346
00347 return 0;
00348 }
00349
00350 int rtnl_link_vlan_get_id(struct rtnl_link *link)
00351 {
00352 struct vlan_info *vi = link->l_info;
00353
00354 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00355 return -NLE_OPNOTSUPP;
00356
00357 if (vi->vi_mask & VLAN_HAS_ID)
00358 return vi->vi_vlan_id;
00359 else
00360 return 0;
00361 }
00362
00363 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
00364 {
00365 struct vlan_info *vi = link->l_info;
00366
00367 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00368 return -NLE_OPNOTSUPP;
00369
00370 vi->vi_flags_mask |= flags;
00371 vi->vi_flags |= flags;
00372 vi->vi_mask |= VLAN_HAS_FLAGS;
00373
00374 return 0;
00375 }
00376
00377 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
00378 {
00379 struct vlan_info *vi = link->l_info;
00380
00381 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00382 return -NLE_OPNOTSUPP;
00383
00384 vi->vi_flags_mask |= flags;
00385 vi->vi_flags &= ~flags;
00386 vi->vi_mask |= VLAN_HAS_FLAGS;
00387
00388 return 0;
00389 }
00390
00391 unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link)
00392 {
00393 struct vlan_info *vi = link->l_info;
00394
00395 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00396 return -NLE_OPNOTSUPP;
00397
00398 return vi->vi_flags;
00399 }
00400
00401 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
00402 uint32_t to)
00403 {
00404 struct vlan_info *vi = link->l_info;
00405
00406 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00407 return -NLE_OPNOTSUPP;
00408
00409 if (from < 0 || from > VLAN_PRIO_MAX)
00410 return -NLE_INVAL;
00411
00412 vi->vi_ingress_qos[from] = to;
00413 vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
00414
00415 return 0;
00416 }
00417
00418 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
00419 {
00420 struct vlan_info *vi = link->l_info;
00421
00422 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00423 return NULL;
00424
00425 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
00426 return vi->vi_ingress_qos;
00427 else
00428 return NULL;
00429 }
00430
00431 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
00432 {
00433 struct vlan_info *vi = link->l_info;
00434
00435 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00436 return -NLE_OPNOTSUPP;
00437
00438 if (to < 0 || to > VLAN_PRIO_MAX)
00439 return -NLE_INVAL;
00440
00441 if (vi->vi_negress >= vi->vi_egress_size) {
00442 int new_size = vi->vi_egress_size + 32;
00443 void *ptr;
00444
00445 ptr = realloc(vi->vi_egress_qos, new_size);
00446 if (!ptr)
00447 return -NLE_NOMEM;
00448
00449 vi->vi_egress_qos = ptr;
00450 vi->vi_egress_size = new_size;
00451 }
00452
00453 vi->vi_egress_qos[vi->vi_negress].vm_from = from;
00454 vi->vi_egress_qos[vi->vi_negress].vm_to = to;
00455 vi->vi_negress++;
00456 vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
00457
00458 return 0;
00459 }
00460
00461 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
00462 int *negress)
00463 {
00464 struct vlan_info *vi = link->l_info;
00465
00466 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
00467 return NULL;
00468
00469 if (negress == NULL)
00470 return NULL;
00471
00472 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
00473 *negress = vi->vi_negress;
00474 return vi->vi_egress_qos;
00475 } else {
00476 *negress = 0;
00477 return NULL;
00478 }
00479 }
00480
00481 static void __init vlan_init(void)
00482 {
00483 rtnl_link_register_info(&vlan_info_ops);
00484 }
00485
00486 static void __exit vlan_exit(void)
00487 {
00488 rtnl_link_unregister_info(&vlan_info_ops);
00489 }
00490
00491