1 /* 2 * lib/fib_lookup/lookup.c FIB Lookup 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-2008 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @defgroup fib_lookup FIB Lookup 14 * @brief 15 * @{ 16 */ 17 18 #include <netlink-local.h> 19 #include <netlink/netlink.h> 20 #include <netlink/attr.h> 21 #include <netlink/utils.h> 22 #include <netlink/object.h> 23 #include <netlink/route/rtnl.h> 24 #include <netlink/route/route.h> 25 #include <netlink/fib_lookup/request.h> 26 #include <netlink/fib_lookup/lookup.h> 27 28 /** @cond SKIP */ 29 static struct nl_cache_ops fib_lookup_ops; 30 static struct nl_object_ops result_obj_ops; 31 32 /* not exported so far */ 33 struct fib_result_nl { 34 uint32_t fl_addr; /* To be looked up*/ 35 uint32_t fl_fwmark; 36 unsigned char fl_tos; 37 unsigned char fl_scope; 38 unsigned char tb_id_in; 39 40 unsigned char tb_id; /* Results */ 41 unsigned char prefixlen; 42 unsigned char nh_sel; 43 unsigned char type; 44 unsigned char scope; 45 int err; 46 }; 47 /** @endcond */ 48 49 static void result_free_data(struct nl_object *obj) 50 { 51 struct flnl_result *res = nl_object_priv(obj); 52 53 if (res && res->fr_req) 54 nl_object_put(OBJ_CAST(res->fr_req)); 55 } 56 57 static int result_clone(struct nl_object *_dst, struct nl_object *_src) 58 { 59 struct flnl_result *dst = nl_object_priv(_dst); 60 struct flnl_result *src = nl_object_priv(_src); 61 62 if (src->fr_req) 63 if (!(dst->fr_req = (struct flnl_request *) 64 nl_object_clone(OBJ_CAST(src->fr_req)))) 65 return -NLE_NOMEM; 66 67 return 0; 68 } 69 70 static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, 71 struct nlmsghdr *n, struct nl_parser_param *pp) 72 { 73 struct flnl_result *res; 74 struct fib_result_nl *fr; 75 struct nl_addr *addr; 76 int err = -NLE_INVAL; 77 78 res = flnl_result_alloc(); 79 if (!res) 80 goto errout; 81 82 res->ce_msgtype = n->nlmsg_type; 83 84 res->fr_req = flnl_request_alloc(); 85 if (!res->fr_req) 86 goto errout; 87 88 fr = nlmsg_data(n); 89 addr = nl_addr_build(AF_INET, &fr->fl_addr, 4); 90 if (!addr) 91 goto errout; 92 err = flnl_request_set_addr(res->fr_req, addr); 93 nl_addr_put(addr); 94 if (err < 0) 95 goto errout; 96 97 flnl_request_set_fwmark(res->fr_req, fr->fl_fwmark); 98 flnl_request_set_tos(res->fr_req, fr->fl_tos); 99 flnl_request_set_scope(res->fr_req, fr->fl_scope); 100 flnl_request_set_table(res->fr_req, fr->tb_id_in); 101 102 res->fr_table_id = fr->tb_id; 103 res->fr_prefixlen = fr->prefixlen; 104 res->fr_nh_sel = fr->nh_sel; 105 res->fr_type = fr->type; 106 res->fr_scope = fr->scope; 107 res->fr_error = fr->err; 108 109 err = pp->pp_cb((struct nl_object *) res, pp); 110 if (err < 0) 111 goto errout; 112 113 /* REAL HACK, fib_lookup doesn't support ACK nor does it 114 * send a DONE message, enforce end of message stream 115 * after just the first message */ 116 err = NL_STOP; 117 118 errout: 119 flnl_result_put(res); 120 return err; 121 } 122 123 static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p) 124 { 125 struct flnl_result *res = (struct flnl_result *) obj; 126 char buf[128]; 127 128 nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n", 129 rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)), 130 res->fr_prefixlen, res->fr_nh_sel); 131 nl_dump_line(p, "type %s ", 132 nl_rtntype2str(res->fr_type, buf, sizeof(buf))); 133 nl_dump(p, "scope %s error %s (%d)\n", 134 rtnl_scope2str(res->fr_scope, buf, sizeof(buf)), 135 strerror(-res->fr_error), res->fr_error); 136 } 137 138 static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p) 139 { 140 result_dump_line(obj, p); 141 } 142 143 static int result_compare(struct nl_object *_a, struct nl_object *_b, 144 uint32_t attrs, int flags) 145 { 146 return 0; 147 } 148 149 /** 150 * @name Allocation/Freeing 151 * @{ 152 */ 153 154 struct flnl_result *flnl_result_alloc(void) 155 { 156 return (struct flnl_result *) nl_object_alloc(&result_obj_ops); 157 } 158 159 void flnl_result_put(struct flnl_result *res) 160 { 161 nl_object_put((struct nl_object *) res); 162 } 163 164 /** @} */ 165 166 /** 167 * @name Cache Management 168 * @{ 169 */ 170 171 /** 172 * Allocate lookup result cache. 173 * 174 * Allocates a new lookup result cache and initializes it properly. 175 * 176 * @note Free the memory after usage using nl_cache_destroy_and_free(). 177 * @return Newly allocated cache or NULL if an error occured. 178 */ 179 struct nl_cache *flnl_result_alloc_cache(void) 180 { 181 return nl_cache_alloc(&fib_lookup_ops); 182 } 183 184 /** @} */ 185 186 /** 187 * @name Lookup 188 * @{ 189 */ 190 191 /** 192 * Builds a netlink request message to do a lookup 193 * @arg req Requested match. 194 * @arg flags additional netlink message flags 195 * 196 * Builds a new netlink message requesting a change of link attributes. 197 * The netlink message header isn't fully equipped with all relevant 198 * fields and must be sent out via nl_send_auto_complete() or 199 * supplemented as needed. 200 * \a old must point to a link currently configured in the kernel 201 * and \a tmpl must contain the attributes to be changed set via 202 * \c rtnl_link_set_* functions. 203 * 204 * @return New netlink message 205 * @note Not all attributes can be changed, see 206 * \ref link_changeable "Changeable Attributes" for more details. 207 */ 208 int flnl_lookup_build_request(struct flnl_request *req, int flags, 209 struct nl_msg **result) 210 { 211 struct nl_msg *msg; 212 struct nl_addr *addr; 213 uint64_t fwmark; 214 int tos, scope, table; 215 struct fib_result_nl fr = {0}; 216 217 fwmark = flnl_request_get_fwmark(req); 218 tos = flnl_request_get_tos(req); 219 scope = flnl_request_get_scope(req); 220 table = flnl_request_get_table(req); 221 222 fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0; 223 fr.fl_tos = tos >= 0 ? tos : 0; 224 fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE; 225 fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC; 226 227 addr = flnl_request_get_addr(req); 228 if (!addr) 229 return -NLE_MISSING_ATTR; 230 231 fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); 232 233 msg = nlmsg_alloc_simple(0, flags); 234 if (!msg) 235 return -NLE_NOMEM; 236 237 if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0) 238 goto errout; 239 240 *result = msg; 241 return 0; 242 243 errout: 244 nlmsg_free(msg); 245 return -NLE_MSGSIZE; 246 } 247 248 /** 249 * Perform FIB Lookup 250 * @arg sk Netlink socket. 251 * @arg req Lookup request object. 252 * @arg cache Cache for result. 253 * 254 * Builds a netlink message to request a FIB lookup, waits for the 255 * reply and adds the result to the specified cache. 256 * 257 * @return 0 on success or a negative error code. 258 */ 259 int flnl_lookup(struct nl_sock *sk, struct flnl_request *req, 260 struct nl_cache *cache) 261 { 262 struct nl_msg *msg; 263 int err; 264 265 if ((err = flnl_lookup_build_request(req, 0, &msg)) < 0) 266 return err; 267 268 err = nl_send_auto_complete(sk, msg); 269 nlmsg_free(msg); 270 if (err < 0) 271 return err; 272 273 return nl_cache_pickup(sk, cache); 274 } 275 276 /** @} */ 277 278 /** 279 * @name Attribute Access 280 * @{ 281 */ 282 283 int flnl_result_get_table_id(struct flnl_result *res) 284 { 285 return res->fr_table_id; 286 } 287 288 int flnl_result_get_prefixlen(struct flnl_result *res) 289 { 290 return res->fr_prefixlen; 291 } 292 293 int flnl_result_get_nexthop_sel(struct flnl_result *res) 294 { 295 return res->fr_nh_sel; 296 } 297 298 int flnl_result_get_type(struct flnl_result *res) 299 { 300 return res->fr_type; 301 } 302 303 int flnl_result_get_scope(struct flnl_result *res) 304 { 305 return res->fr_scope; 306 } 307 308 int flnl_result_get_error(struct flnl_result *res) 309 { 310 return res->fr_error; 311 } 312 313 /** @} */ 314 315 static struct nl_object_ops result_obj_ops = { 316 .oo_name = "fib_lookup/result", 317 .oo_size = sizeof(struct flnl_result), 318 .oo_free_data = result_free_data, 319 .oo_clone = result_clone, 320 .oo_dump = { 321 [NL_DUMP_LINE] = result_dump_line, 322 [NL_DUMP_DETAILS] = result_dump_details, 323 }, 324 .oo_compare = result_compare, 325 }; 326 327 static struct nl_cache_ops fib_lookup_ops = { 328 .co_name = "fib_lookup/fib_lookup", 329 .co_hdrsize = sizeof(struct fib_result_nl), 330 .co_msgtypes = { 331 { 0, NL_ACT_UNSPEC, "any" }, 332 END_OF_MSGTYPES_LIST, 333 }, 334 .co_protocol = NETLINK_FIB_LOOKUP, 335 .co_msg_parser = result_msg_parser, 336 .co_obj_ops = &result_obj_ops, 337 }; 338 339 static void __init fib_lookup_init(void) 340 { 341 nl_cache_mngt_register(&fib_lookup_ops); 342 } 343 344 static void __exit fib_lookup_exit(void) 345 { 346 nl_cache_mngt_unregister(&fib_lookup_ops); 347 } 348 349 /** @} */ 350