libnl 1.1
|
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-2007 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup link_info 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/info-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 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 nl_errno(ENOMEM); 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 nl_error(EINVAL, "Malformed mapping"); 00123 00124 map = nla_data(nla); 00125 if (map->from < 0 || map->from > VLAN_PRIO_MAX) { 00126 return nl_error(EINVAL, "VLAN prio %d out of " 00127 "range", map->from); 00128 } 00129 00130 vi->vi_ingress_qos[map->from] = map->to; 00131 } 00132 00133 vi->vi_mask |= VLAN_HAS_INGRESS_QOS; 00134 } 00135 00136 if (tb[IFLA_VLAN_EGRESS_QOS]) { 00137 struct ifla_vlan_qos_mapping *map; 00138 struct nlattr *nla; 00139 int remaining, i = 0; 00140 00141 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { 00142 if (nla_len(nla) < sizeof(*map)) 00143 return nl_error(EINVAL, "Malformed mapping"); 00144 i++; 00145 } 00146 00147 /* align to have a little reserve */ 00148 vi->vi_egress_size = (i + 32) & ~31; 00149 vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map)); 00150 if (vi->vi_egress_qos == NULL) 00151 return nl_errno(ENOMEM); 00152 00153 i = 0; 00154 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { 00155 map = nla_data(nla); 00156 NL_DBG(4, "Assigning egress qos mapping %d\n", i); 00157 vi->vi_egress_qos[i].vm_from = map->from; 00158 vi->vi_egress_qos[i++].vm_to = map->to; 00159 } 00160 00161 vi->vi_negress = i; 00162 vi->vi_mask |= VLAN_HAS_EGRESS_QOS; 00163 } 00164 00165 err = 0; 00166 errout: 00167 return err; 00168 } 00169 00170 static void vlan_free(struct rtnl_link *link) 00171 { 00172 struct vlan_info *vi = link->l_info; 00173 00174 if (vi) { 00175 free(vi->vi_egress_qos); 00176 vi->vi_egress_qos = NULL; 00177 } 00178 00179 free(vi); 00180 link->l_info = NULL; 00181 } 00182 00183 static int vlan_dump_brief(struct rtnl_link *link, struct nl_dump_params *p, 00184 int line) 00185 { 00186 struct vlan_info *vi = link->l_info; 00187 00188 dp_dump(p, "vlan-id %d", vi->vi_vlan_id); 00189 00190 return line; 00191 } 00192 00193 static int vlan_dump_full(struct rtnl_link *link, struct nl_dump_params *p, 00194 int line) 00195 { 00196 struct vlan_info *vi = link->l_info; 00197 int i, printed; 00198 char buf[64]; 00199 00200 rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf)); 00201 dp_dump_line(p, line++, " vlan-info id %d <%s>\n", 00202 vi->vi_vlan_id, buf); 00203 00204 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { 00205 dp_dump_line(p, line++, 00206 " ingress vlan prio -> qos/socket prio mapping:\n"); 00207 for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) { 00208 if (vi->vi_ingress_qos[i]) { 00209 if (printed == 0) { 00210 dp_new_line(p, line); 00211 dp_dump(p, " "); 00212 } 00213 dp_dump(p, "%x -> %#08x, ", 00214 i, vi->vi_ingress_qos[i]); 00215 if (printed++ == 3) { 00216 dp_dump(p, "\n"); 00217 printed = 0; 00218 } 00219 } 00220 } 00221 00222 if (printed > 0 && printed != 4) 00223 dp_dump(p, "\n"); 00224 } 00225 00226 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { 00227 dp_dump_line(p, line++, 00228 " egress qos/socket prio -> vlan prio mapping:\n"); 00229 for (i = 0, printed = 0; i < vi->vi_negress; i++) { 00230 if (printed == 0) { 00231 dp_new_line(p, line); 00232 dp_dump(p, " "); 00233 } 00234 dp_dump(p, "%#08x -> %x, ", 00235 vi->vi_egress_qos[i].vm_from, 00236 vi->vi_egress_qos[i].vm_to); 00237 if (printed++ == 3) { 00238 dp_dump(p, "\n"); 00239 printed = 0; 00240 } 00241 } 00242 00243 if (printed > 0 && printed != 4) 00244 dp_dump(p, "\n"); 00245 } 00246 00247 return line; 00248 } 00249 00250 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) 00251 { 00252 struct vlan_info *vdst, *vsrc = src->l_info; 00253 int err; 00254 00255 dst->l_info = NULL; 00256 if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0) 00257 return err; 00258 vdst = dst->l_info; 00259 00260 vdst->vi_egress_qos = calloc(vsrc->vi_egress_size, 00261 sizeof(struct vlan_map)); 00262 if (!vdst->vi_egress_qos) 00263 return nl_errno(ENOMEM); 00264 00265 memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos, 00266 vsrc->vi_egress_size * sizeof(struct vlan_map)); 00267 00268 return 0; 00269 } 00270 00271 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) 00272 { 00273 struct vlan_info *vi = link->l_info; 00274 struct nlattr *data; 00275 00276 if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) 00277 return nl_errno(ENOBUFS); 00278 00279 if (vi->vi_mask & VLAN_HAS_ID) 00280 NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id); 00281 00282 if (vi->vi_mask & VLAN_HAS_FLAGS) { 00283 struct ifla_vlan_flags flags = { 00284 .flags = vi->vi_flags, 00285 .mask = vi->vi_flags_mask, 00286 }; 00287 00288 NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); 00289 } 00290 00291 if (vi->vi_mask & VLAN_HAS_INGRESS_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_INGRESS_QOS))) 00297 goto nla_put_failure; 00298 00299 for (i = 0; i <= VLAN_PRIO_MAX; i++) { 00300 if (vi->vi_ingress_qos[i]) { 00301 map.from = i; 00302 map.to = vi->vi_ingress_qos[i]; 00303 00304 NLA_PUT(msg, i, sizeof(map), &map); 00305 } 00306 } 00307 00308 nla_nest_end(msg, qos); 00309 } 00310 00311 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { 00312 struct ifla_vlan_qos_mapping map; 00313 struct nlattr *qos; 00314 int i; 00315 00316 if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) 00317 goto nla_put_failure; 00318 00319 for (i = 0; i < vi->vi_negress; i++) { 00320 map.from = vi->vi_egress_qos[i].vm_from; 00321 map.to = vi->vi_egress_qos[i].vm_to; 00322 00323 NLA_PUT(msg, i, sizeof(map), &map); 00324 } 00325 00326 nla_nest_end(msg, qos); 00327 } 00328 00329 nla_nest_end(msg, data); 00330 00331 nla_put_failure: 00332 00333 return 0; 00334 } 00335 00336 static struct rtnl_link_info_ops vlan_info_ops = { 00337 .io_name = "vlan", 00338 .io_alloc = vlan_alloc, 00339 .io_parse = vlan_parse, 00340 .io_dump[NL_DUMP_BRIEF] = vlan_dump_brief, 00341 .io_dump[NL_DUMP_FULL] = vlan_dump_full, 00342 .io_clone = vlan_clone, 00343 .io_put_attrs = vlan_put_attrs, 00344 .io_free = vlan_free, 00345 }; 00346 00347 int rtnl_link_vlan_set_id(struct rtnl_link *link, int id) 00348 { 00349 struct vlan_info *vi = link->l_info; 00350 00351 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00352 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00353 00354 vi->vi_vlan_id = id; 00355 vi->vi_mask |= VLAN_HAS_ID; 00356 00357 return 0; 00358 } 00359 00360 int rtnl_link_vlan_get_id(struct rtnl_link *link) 00361 { 00362 struct vlan_info *vi = link->l_info; 00363 00364 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00365 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00366 00367 if (vi->vi_mask & VLAN_HAS_ID) 00368 return vi->vi_vlan_id; 00369 else 00370 return 0; 00371 } 00372 00373 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) 00374 { 00375 struct vlan_info *vi = link->l_info; 00376 00377 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00378 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00379 00380 vi->vi_flags_mask |= flags; 00381 vi->vi_flags |= flags; 00382 vi->vi_mask |= VLAN_HAS_FLAGS; 00383 00384 return 0; 00385 } 00386 00387 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) 00388 { 00389 struct vlan_info *vi = link->l_info; 00390 00391 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00392 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00393 00394 vi->vi_flags_mask |= flags; 00395 vi->vi_flags &= ~flags; 00396 vi->vi_mask |= VLAN_HAS_FLAGS; 00397 00398 return 0; 00399 } 00400 00401 unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link) 00402 { 00403 struct vlan_info *vi = link->l_info; 00404 00405 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00406 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00407 00408 return vi->vi_flags; 00409 } 00410 00411 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from, 00412 uint32_t to) 00413 { 00414 struct vlan_info *vi = link->l_info; 00415 00416 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00417 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00418 00419 if (from < 0 || from > VLAN_PRIO_MAX) 00420 return nl_error(EINVAL, "Invalid vlan prio 0..%d", 00421 VLAN_PRIO_MAX); 00422 00423 vi->vi_ingress_qos[from] = to; 00424 vi->vi_mask |= VLAN_HAS_INGRESS_QOS; 00425 00426 return 0; 00427 } 00428 00429 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link) 00430 { 00431 struct vlan_info *vi = link->l_info; 00432 00433 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) { 00434 nl_error(EOPNOTSUPP, "Not a VLAN link"); 00435 return NULL; 00436 } 00437 00438 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) 00439 return vi->vi_ingress_qos; 00440 else 00441 return NULL; 00442 } 00443 00444 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to) 00445 { 00446 struct vlan_info *vi = link->l_info; 00447 00448 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 00449 return nl_error(EOPNOTSUPP, "Not a VLAN link"); 00450 00451 if (to < 0 || to > VLAN_PRIO_MAX) 00452 return nl_error(EINVAL, "Invalid vlan prio 0..%d", 00453 VLAN_PRIO_MAX); 00454 00455 if (vi->vi_negress >= vi->vi_egress_size) { 00456 int new_size = vi->vi_egress_size + 32; 00457 void *ptr; 00458 00459 ptr = realloc(vi->vi_egress_qos, new_size); 00460 if (!ptr) 00461 return nl_errno(ENOMEM); 00462 00463 vi->vi_egress_qos = ptr; 00464 vi->vi_egress_size = new_size; 00465 } 00466 00467 vi->vi_egress_qos[vi->vi_negress].vm_from = from; 00468 vi->vi_egress_qos[vi->vi_negress].vm_to = to; 00469 vi->vi_negress++; 00470 vi->vi_mask |= VLAN_HAS_EGRESS_QOS; 00471 00472 return 0; 00473 } 00474 00475 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link, 00476 int *negress) 00477 { 00478 struct vlan_info *vi = link->l_info; 00479 00480 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) { 00481 nl_error(EOPNOTSUPP, "Not a VLAN link"); 00482 return NULL; 00483 } 00484 00485 if (negress == NULL) { 00486 nl_error(EINVAL, "Require pointer to store negress"); 00487 return NULL; 00488 } 00489 00490 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { 00491 *negress = vi->vi_negress; 00492 return vi->vi_egress_qos; 00493 } else { 00494 *negress = 0; 00495 return NULL; 00496 } 00497 } 00498 00499 static void __init vlan_init(void) 00500 { 00501 rtnl_link_register_info(&vlan_info_ops); 00502 } 00503 00504 static void __exit vlan_exit(void) 00505 { 00506 rtnl_link_unregister_info(&vlan_info_ops); 00507 } 00508 00509 /** @} */