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  * @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 /** @} */