libnl 3.0
|
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 * @brief 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 /** @cond SKIP */ 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 /** @endcond */ 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 /* align to have a little reserve */ 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 /** @} */