libnl
3.2.3
|
00001 /* 00002 * lib/route/link/vlan.c VLAN Link Info 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup link 00014 * @defgroup vlan VLAN 00015 * Virtual LAN link module 00016 * 00017 * @details 00018 * \b Link Type Name: "vlan" 00019 * 00020 * @route_doc{link_vlan, VLAN Documentation} 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 /** @cond SKIP */ 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 /** @endcond */ 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 /* align to have a little reserve */ 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 /** @cond SKIP */ 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 /** @endcond */ 00336 00337 /** 00338 * @name VLAN Object 00339 * @{ 00340 */ 00341 00342 /** 00343 * Check if link is a VLAN link 00344 * @arg link Link object 00345 * 00346 * @return True if link is a VLAN link, otherwise false is returned. 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 * Set VLAN ID 00355 * @arg link Link object 00356 * @arg id VLAN identifier 00357 * 00358 * @return 0 on success or a negative error code 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 * Get VLAN Id 00374 * @arg link Link object 00375 * 00376 * @return VLAN id, 0 if not set or a negative error code. 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 * Set VLAN flags 00392 * @arg link Link object 00393 * @arg flags VLAN flags 00394 * 00395 * @return 0 on success or a negative error code. 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 * Unset VLAN flags 00412 * @arg link Link object 00413 * @arg flags VLAN flags 00414 * 00415 * @return 0 on success or a negative error code. 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 * Get VLAN flags 00432 * @arg link Link object 00433 * 00434 * @return VLAN flags, 0 if none set, or a negative error code. 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 * @name Quality of Service 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 * @name Flag Translation 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 /** @} */