Home | History | Annotate | Download | only in route
      1 /*
      2  * lib/route/classifier.c       Classifier
      3  *
      4  *	This library is free software; you can redistribute it and/or
      5  *	modify it under the terms of the GNU Lesser General Public
      6  *	License as published by the Free Software Foundation version 2.1
      7  *	of the License.
      8  *
      9  * Copyright (c) 2003-2013 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup tc
     14  * @defgroup cls Classifiers
     15  * @{
     16  */
     17 
     18 #include <netlink-private/netlink.h>
     19 #include <netlink-private/tc.h>
     20 #include <netlink/netlink.h>
     21 #include <netlink/utils.h>
     22 #include <netlink-private/route/tc-api.h>
     23 #include <netlink/route/classifier.h>
     24 #include <netlink/route/link.h>
     25 
     26 /** @cond SKIP */
     27 #define CLS_ATTR_PRIO		(TCA_ATTR_MAX << 1)
     28 #define CLS_ATTR_PROTOCOL	(TCA_ATTR_MAX << 2)
     29 /** @endcond */
     30 
     31 static struct nl_object_ops cls_obj_ops;
     32 static struct nl_cache_ops rtnl_cls_ops;
     33 
     34 
     35 static int cls_build(struct rtnl_cls *cls, int type, int flags,
     36 		     struct nl_msg **result)
     37 {
     38 	int err, prio, proto;
     39 	struct tcmsg *tchdr;
     40 	uint32_t required = TCA_ATTR_IFINDEX;
     41 
     42 	if ((cls->ce_mask & required) != required) {
     43 		APPBUG("ifindex must be specified");
     44 		return -NLE_MISSING_ATTR;
     45 	}
     46 
     47 	err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result);
     48 	if (err < 0)
     49 		return err;
     50 
     51 	tchdr = nlmsg_data(nlmsg_hdr(*result));
     52 	prio = rtnl_cls_get_prio(cls);
     53 	proto = rtnl_cls_get_protocol(cls);
     54 	tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
     55 
     56 	return 0;
     57 }
     58 
     59 /**
     60  * @name Allocation/Freeing
     61  * @{
     62  */
     63 
     64 struct rtnl_cls *rtnl_cls_alloc(void)
     65 {
     66 	struct rtnl_tc *tc;
     67 
     68 	tc = TC_CAST(nl_object_alloc(&cls_obj_ops));
     69 	if (tc)
     70 		tc->tc_type = RTNL_TC_TYPE_CLS;
     71 
     72 	return (struct rtnl_cls *) tc;
     73 }
     74 
     75 void rtnl_cls_put(struct rtnl_cls *cls)
     76 {
     77 	nl_object_put((struct nl_object *) cls);
     78 }
     79 
     80 /** @} */
     81 
     82 /**
     83  * @name Attributes
     84  * @{
     85  */
     86 
     87 void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio)
     88 {
     89 	cls->c_prio = prio;
     90 	cls->ce_mask |= CLS_ATTR_PRIO;
     91 }
     92 
     93 uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls)
     94 {
     95 	if (cls->ce_mask & CLS_ATTR_PRIO)
     96 		return cls->c_prio;
     97 	else
     98 		return 0;
     99 }
    100 
    101 void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol)
    102 {
    103 	cls->c_protocol = protocol;
    104 	cls->ce_mask |= CLS_ATTR_PROTOCOL;
    105 }
    106 
    107 uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
    108 {
    109 	if (cls->ce_mask & CLS_ATTR_PROTOCOL)
    110 		return cls->c_protocol;
    111 	else
    112 		return ETH_P_ALL;
    113 }
    114 
    115 /** @} */
    116 
    117 
    118 /**
    119  * @name Addition/Modification/Deletion
    120  * @{
    121  */
    122 
    123 /**
    124  * Build a netlink message requesting the addition of a classifier
    125  * @arg cls		Classifier to add
    126  * @arg flags		Additional netlink message flags
    127  * @arg result		Pointer to store resulting netlink message
    128  *
    129  * The behaviour of this function is identical to rtnl_cls_add() with
    130  * the exception that it will not send the message but return it int the
    131  * provided return pointer instead.
    132  *
    133  * @see rtnl_cls_add()
    134  *
    135  * @return 0 on success or a negative error code.
    136  */
    137 int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags,
    138 			       struct nl_msg **result)
    139 {
    140 	if (!(flags & NLM_F_CREATE) && !(cls->ce_mask & CLS_ATTR_PRIO)) {
    141 		APPBUG("prio must be specified if not a new classifier");
    142 		return -NLE_MISSING_ATTR;
    143 	}
    144 
    145 	return cls_build(cls, RTM_NEWTFILTER, flags, result);
    146 }
    147 
    148 /**
    149  * Add/Update classifier
    150  * @arg sk		Netlink socket
    151  * @arg cls		Classifier to add/update
    152  * @arg flags		Additional netlink message flags
    153  *
    154  * Builds a \c RTM_NEWTFILTER netlink message requesting the addition
    155  * of a new classifier and sends the message to the kernel. The
    156  * configuration of the classifier is derived from the attributes of
    157  * the specified traffic class.
    158  *
    159  * The following flags may be specified:
    160  *  - \c NLM_F_CREATE:  Create classifier if it does not exist,
    161  *                      otherwise -NLE_OBJ_NOTFOUND is returned.
    162  *  - \c NLM_F_EXCL:    Return -NLE_EXISTS if a classifier with
    163  *                      matching handle exists already.
    164  *
    165  * Existing classifiers with matching handles will be updated, unless
    166  * the flag \c NLM_F_EXCL is specified. If no matching classifier
    167  * exists, it will be created if the flag \c NLM_F_CREATE is set,
    168  * otherwise the error -NLE_OBJ_NOTFOUND is returned.
    169  *
    170  * If the parent qdisc does not support classes, the error
    171  * \c NLE_OPNOTSUPP is returned.
    172  *
    173  * After sending, the function will wait for the ACK or an eventual
    174  * error message to be received and will therefore block until the
    175  * operation has been completed.
    176  *
    177  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
    178  *       this function to return immediately after sending. In this case,
    179  *       it is the responsibility of the caller to handle any error
    180  *       messages returned.
    181  *
    182  * @return 0 on success or a negative error code.
    183  */
    184 int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
    185 {
    186 	struct nl_msg *msg;
    187 	int err;
    188 
    189 	if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0)
    190 		return err;
    191 
    192 	return nl_send_sync(sk, msg);
    193 }
    194 
    195 /**
    196  * Build a netlink message to change classifier attributes
    197  * @arg cls		classifier to change
    198  * @arg flags		additional netlink message flags
    199  * @arg result		Pointer to store resulting message.
    200  *
    201  * Builds a new netlink message requesting a change of a neigh
    202  * attributes. The netlink message header isn't fully equipped with
    203  * all relevant fields and must thus be sent out via nl_send_auto_complete()
    204  * or supplemented as needed.
    205  *
    206  * @return 0 on success or a negative error code.
    207  */
    208 int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags,
    209 				  struct nl_msg **result)
    210 {
    211 	return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result);
    212 }
    213 
    214 /**
    215  * Change a classifier
    216  * @arg sk		Netlink socket.
    217  * @arg cls		classifier to change
    218  * @arg flags		additional netlink message flags
    219  *
    220  * Builds a netlink message by calling rtnl_cls_build_change_request(),
    221  * sends the request to the kernel and waits for the next ACK to be
    222  * received and thus blocks until the request has been processed.
    223  *
    224  * @return 0 on sucess or a negative error if an error occured.
    225  */
    226 int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
    227 {
    228 	struct nl_msg *msg;
    229 	int err;
    230 
    231 	if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0)
    232 		return err;
    233 
    234 	return nl_send_sync(sk, msg);
    235 }
    236 
    237 /**
    238  * Build netlink message requesting the deletion of a classifier
    239  * @arg cls		Classifier to delete
    240  * @arg flags		Additional netlink message flags
    241  * @arg result		Pointer to store resulting netlink message
    242  *
    243  * The behaviour of this function is identical to rtnl_cls_delete() with
    244  * the exception that it will not send the message but return it in the
    245  * provided return pointer instead.
    246  *
    247  * @see rtnl_cls_delete()
    248  *
    249  * @return 0 on success or a negative error code.
    250  */
    251 int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags,
    252 				  struct nl_msg **result)
    253 {
    254 	uint32_t required = CLS_ATTR_PRIO;
    255 
    256 	if ((cls->ce_mask & required) != required) {
    257 		APPBUG("prio must be specified");
    258 		return -NLE_MISSING_ATTR;
    259 	}
    260 
    261 	return cls_build(cls, RTM_DELTFILTER, flags, result);
    262 }
    263 
    264 /**
    265  * Delete classifier
    266  * @arg sk		Netlink socket
    267  * @arg cls		Classifier to delete
    268  * @arg flags		Additional netlink message flags
    269  *
    270  * Builds a \c RTM_DELTFILTER netlink message requesting the deletion
    271  * of a classifier and sends the message to the kernel.
    272  *
    273  * The message is constructed out of the following attributes:
    274  * - \c ifindex (required)
    275  * - \c prio (required)
    276  * - \c protocol (required)
    277  * - \c handle (required)
    278  * - \c parent (optional, if not specified parent equals root-qdisc)
    279  * - \c kind (optional, must match if provided)
    280  *
    281  * All other classifier attributes including all class type specific
    282  * attributes are ignored.
    283  *
    284  * After sending, the function will wait for the ACK or an eventual
    285  * error message to be received and will therefore block until the
    286  * operation has been completed.
    287  *
    288  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
    289  *       this function to return immediately after sending. In this case,
    290  *       it is the responsibility of the caller to handle any error
    291  *       messages returned.
    292  *
    293  * @return 0 on success or a negative error code.
    294  */
    295 int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
    296 {
    297 	struct nl_msg *msg;
    298 	int err;
    299 
    300 	if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0)
    301 		return err;
    302 
    303 	return nl_send_sync(sk, msg);
    304 }
    305 
    306 /** @} */
    307 
    308 /**
    309  * @name Cache Related Functions
    310  * @{
    311  */
    312 
    313 /**
    314  * Allocate a cache and fill it with all configured classifiers
    315  * @arg sk		Netlink socket
    316  * @arg ifindex		Interface index of the network device
    317  * @arg parent		Parent qdisc/traffic class class
    318  * @arg result		Pointer to store the created cache
    319  *
    320  * Allocates a new classifier cache and fills it with a list of all
    321  * configured classifier attached to the specified parent qdisc/traffic
    322  * class on the specified network device. Release the cache with
    323  * nl_cache_free().
    324  *
    325  * @return 0 on success or a negative error code.
    326  */
    327 int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,			 struct nl_cache **result)
    328 {
    329 	struct nl_cache * cache;
    330 	int err;
    331 
    332 	if (!(cache = nl_cache_alloc(&rtnl_cls_ops)))
    333 		return -NLE_NOMEM;
    334 
    335 	cache->c_iarg1 = ifindex;
    336 	cache->c_iarg2 = parent;
    337 
    338 	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
    339 		nl_cache_free(cache);
    340 		return err;
    341 	}
    342 
    343 	*result = cache;
    344 	return 0;
    345 }
    346 
    347 /** @} */
    348 
    349 static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
    350 {
    351 	struct rtnl_cls *cls = (struct rtnl_cls *) tc;
    352 	char buf[32];
    353 
    354 	nl_dump(p, " prio %u protocol %s", cls->c_prio,
    355 		nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf)));
    356 }
    357 
    358 static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
    359 			  struct nlmsghdr *nlh, struct nl_parser_param *pp)
    360 {
    361 	struct rtnl_cls *cls;
    362 	int err;
    363 
    364 	if (!(cls = rtnl_cls_alloc()))
    365 		return -NLE_NOMEM;
    366 
    367 	if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(cls))) < 0)
    368 		goto errout;
    369 
    370 	cls->c_prio = TC_H_MAJ(cls->c_info) >> 16;
    371 	if (cls->c_prio)
    372 		cls->ce_mask |= CLS_ATTR_PRIO;
    373 	cls->c_protocol = ntohs(TC_H_MIN(cls->c_info));
    374 	if (cls->c_protocol)
    375 		cls->ce_mask |= CLS_ATTR_PROTOCOL;
    376 
    377 	err = pp->pp_cb(OBJ_CAST(cls), pp);
    378 errout:
    379 	rtnl_cls_put(cls);
    380 
    381 	return err;
    382 }
    383 
    384 static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
    385 {
    386 	struct tcmsg tchdr = {
    387 		.tcm_family = AF_UNSPEC,
    388 		.tcm_ifindex = cache->c_iarg1,
    389 		.tcm_parent = cache->c_iarg2,
    390 	};
    391 
    392 	return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
    393 			      sizeof(tchdr));
    394 }
    395 
    396 static struct rtnl_tc_type_ops cls_ops = {
    397 	.tt_type		= RTNL_TC_TYPE_CLS,
    398 	.tt_dump_prefix		= "cls",
    399 	.tt_dump = {
    400 		[NL_DUMP_LINE]	= cls_dump_line,
    401 	},
    402 };
    403 
    404 static struct nl_cache_ops rtnl_cls_ops = {
    405 	.co_name		= "route/cls",
    406 	.co_hdrsize		= sizeof(struct tcmsg),
    407 	.co_msgtypes		= {
    408 					{ RTM_NEWTFILTER, NL_ACT_NEW, "new" },
    409 					{ RTM_DELTFILTER, NL_ACT_DEL, "del" },
    410 					{ RTM_GETTFILTER, NL_ACT_GET, "get" },
    411 					END_OF_MSGTYPES_LIST,
    412 				  },
    413 	.co_protocol		= NETLINK_ROUTE,
    414 	.co_groups		= tc_groups,
    415 	.co_request_update	= cls_request_update,
    416 	.co_msg_parser		= cls_msg_parser,
    417 	.co_obj_ops		= &cls_obj_ops,
    418 };
    419 
    420 static struct nl_object_ops cls_obj_ops = {
    421 	.oo_name		= "route/cls",
    422 	.oo_size		= sizeof(struct rtnl_cls),
    423 	.oo_free_data		= rtnl_tc_free_data,
    424 	.oo_clone		= rtnl_tc_clone,
    425 	.oo_dump = {
    426 	    [NL_DUMP_LINE]	= rtnl_tc_dump_line,
    427 	    [NL_DUMP_DETAILS]	= rtnl_tc_dump_details,
    428 	    [NL_DUMP_STATS]	= rtnl_tc_dump_stats,
    429 	},
    430 	.oo_compare		= rtnl_tc_compare,
    431 	.oo_id_attrs		= (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
    432 };
    433 
    434 static void __init cls_init(void)
    435 {
    436 	rtnl_tc_type_register(&cls_ops);
    437 	nl_cache_mngt_register(&rtnl_cls_ops);
    438 }
    439 
    440 static void __exit cls_exit(void)
    441 {
    442 	nl_cache_mngt_unregister(&rtnl_cls_ops);
    443 	rtnl_tc_type_unregister(&cls_ops);
    444 }
    445 
    446 /** @} */
    447