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