libnl  3.2.3
/build/buildd/libnl3-3.2.3/lib/route/link/vlan.c
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 /** @} */