libnl 1.1

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