swlib.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*
  2. * swlib.c: Switch configuration API (user space part)
  3. *
  4. * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public License
  8. * version 2.1 as published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include <inttypes.h>
  19. #include <errno.h>
  20. #include <stdint.h>
  21. #include <getopt.h>
  22. #include <sys/types.h>
  23. #include <sys/socket.h>
  24. #include <linux/switch.h>
  25. #include "swlib.h"
  26. //#define DEBUG 1
  27. #ifdef DEBUG
  28. #define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__)
  29. #else
  30. #define DPRINTF(fmt, ...) do {} while (0)
  31. #endif
  32. static struct nl_handle *handle;
  33. static struct nl_cache *cache;
  34. static struct genl_family *family;
  35. static struct nlattr *tb[SWITCH_ATTR_MAX];
  36. static int refcount = 0;
  37. static struct nla_policy port_policy[] = {
  38. [SWITCH_PORT_ID] = { .type = NLA_U32 },
  39. [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
  40. };
  41. static inline void *
  42. swlib_alloc(size_t size)
  43. {
  44. void *ptr;
  45. ptr = malloc(size);
  46. if (!ptr)
  47. goto done;
  48. memset(ptr, 0, size);
  49. done:
  50. return ptr;
  51. }
  52. static int
  53. wait_handler(struct nl_msg *msg, void *arg)
  54. {
  55. int *finished = arg;
  56. *finished = 1;
  57. return NL_STOP;
  58. }
  59. /* helper function for performing netlink requests */
  60. static int
  61. swlib_call(int cmd, int (*call)(struct nl_msg *, void *),
  62. int (*data)(struct nl_msg *, void *), void *arg)
  63. {
  64. struct nl_msg *msg;
  65. struct nl_cb *cb = NULL;
  66. int finished;
  67. int flags = 0;
  68. int err;
  69. msg = nlmsg_alloc();
  70. if (!msg) {
  71. fprintf(stderr, "Out of memory!\n");
  72. exit(1);
  73. }
  74. if (!data)
  75. flags |= NLM_F_DUMP;
  76. genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family), 0, flags, cmd, 0);
  77. if (data) {
  78. if (data(msg, arg) < 0)
  79. goto nla_put_failure;
  80. }
  81. cb = nl_cb_alloc(NL_CB_CUSTOM);
  82. if (!cb) {
  83. fprintf(stderr, "nl_cb_alloc failed.\n");
  84. exit(1);
  85. }
  86. err = nl_send_auto_complete(handle, msg);
  87. if (err < 0) {
  88. fprintf(stderr, "nl_send_auto_complete failed: %d\n", err);
  89. goto out;
  90. }
  91. finished = 0;
  92. if (call)
  93. nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, call, arg);
  94. if (data)
  95. nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
  96. else
  97. nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
  98. err = nl_recvmsgs(handle, cb);
  99. if (err < 0) {
  100. goto out;
  101. }
  102. if (!finished)
  103. err = nl_wait_for_ack(handle);
  104. out:
  105. if (cb)
  106. nl_cb_put(cb);
  107. nla_put_failure:
  108. nlmsg_free(msg);
  109. return err;
  110. }
  111. static int
  112. send_attr(struct nl_msg *msg, void *arg)
  113. {
  114. struct switch_val *val = arg;
  115. struct switch_attr *attr = val->attr;
  116. NLA_PUT_U32(msg, SWITCH_ATTR_ID, attr->dev->id);
  117. NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, attr->id);
  118. switch(attr->atype) {
  119. case SWLIB_ATTR_GROUP_PORT:
  120. NLA_PUT_U32(msg, SWITCH_ATTR_OP_PORT, val->port_vlan);
  121. break;
  122. case SWLIB_ATTR_GROUP_VLAN:
  123. NLA_PUT_U32(msg, SWITCH_ATTR_OP_VLAN, val->port_vlan);
  124. break;
  125. default:
  126. break;
  127. }
  128. return 0;
  129. nla_put_failure:
  130. return -1;
  131. }
  132. static int
  133. store_port_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val)
  134. {
  135. struct nlattr *p;
  136. int ports = val->attr->dev->ports;
  137. int err = 0;
  138. int remaining;
  139. if (!val->value.ports)
  140. val->value.ports = malloc(sizeof(struct switch_port) * ports);
  141. nla_for_each_nested(p, nla, remaining) {
  142. struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
  143. struct switch_port *port;
  144. if (val->len >= ports)
  145. break;
  146. err = nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, p, port_policy);
  147. if (err < 0)
  148. goto out;
  149. if (!tb[SWITCH_PORT_ID])
  150. continue;
  151. port = &val->value.ports[val->len];
  152. port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
  153. port->flags = 0;
  154. if (tb[SWITCH_PORT_FLAG_TAGGED])
  155. port->flags |= SWLIB_PORT_FLAG_TAGGED;
  156. val->len++;
  157. }
  158. out:
  159. return err;
  160. }
  161. static int
  162. store_val(struct nl_msg *msg, void *arg)
  163. {
  164. struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
  165. struct switch_val *val = arg;
  166. struct switch_attr *attr = val->attr;
  167. if (!val)
  168. goto error;
  169. if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
  170. genlmsg_attrlen(gnlh, 0), NULL) < 0) {
  171. goto error;
  172. }
  173. if (tb[SWITCH_ATTR_OP_VALUE_INT])
  174. val->value.i = nla_get_u32(tb[SWITCH_ATTR_OP_VALUE_INT]);
  175. else if (tb[SWITCH_ATTR_OP_VALUE_STR])
  176. val->value.s = strdup(nla_get_string(tb[SWITCH_ATTR_OP_VALUE_STR]));
  177. else if (tb[SWITCH_ATTR_OP_VALUE_PORTS])
  178. val->err = store_port_val(msg, tb[SWITCH_ATTR_OP_VALUE_PORTS], val);
  179. val->err = 0;
  180. return 0;
  181. error:
  182. return NL_SKIP;
  183. }
  184. int
  185. swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
  186. {
  187. int cmd;
  188. int err;
  189. switch(attr->atype) {
  190. case SWLIB_ATTR_GROUP_GLOBAL:
  191. cmd = SWITCH_CMD_GET_GLOBAL;
  192. break;
  193. case SWLIB_ATTR_GROUP_PORT:
  194. cmd = SWITCH_CMD_GET_PORT;
  195. break;
  196. case SWLIB_ATTR_GROUP_VLAN:
  197. cmd = SWITCH_CMD_GET_VLAN;
  198. break;
  199. default:
  200. return -EINVAL;
  201. }
  202. memset(&val->value, 0, sizeof(val->value));
  203. val->len = 0;
  204. val->attr = attr;
  205. val->err = -EINVAL;
  206. err = swlib_call(cmd, store_val, send_attr, val);
  207. if (!err)
  208. err = val->err;
  209. return err;
  210. }
  211. static int
  212. send_attr_ports(struct nl_msg *msg, struct switch_val *val)
  213. {
  214. struct nlattr *n;
  215. int i;
  216. /* TODO implement multipart? */
  217. if (val->len == 0)
  218. goto done;
  219. n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_PORTS);
  220. if (!n)
  221. goto nla_put_failure;
  222. for (i = 0; i < val->len; i++) {
  223. struct switch_port *port = &val->value.ports[i];
  224. struct nlattr *np;
  225. np = nla_nest_start(msg, SWITCH_ATTR_PORT);
  226. if (!np)
  227. goto nla_put_failure;
  228. NLA_PUT_U32(msg, SWITCH_PORT_ID, port->id);
  229. if (port->flags & SWLIB_PORT_FLAG_TAGGED)
  230. NLA_PUT_FLAG(msg, SWITCH_PORT_FLAG_TAGGED);
  231. nla_nest_end(msg, np);
  232. }
  233. nla_nest_end(msg, n);
  234. done:
  235. return 0;
  236. nla_put_failure:
  237. return -1;
  238. }
  239. static int
  240. send_attr_val(struct nl_msg *msg, void *arg)
  241. {
  242. struct switch_val *val = arg;
  243. struct switch_attr *attr = val->attr;
  244. if (send_attr(msg, arg))
  245. goto nla_put_failure;
  246. switch(attr->type) {
  247. case SWITCH_TYPE_NOVAL:
  248. break;
  249. case SWITCH_TYPE_INT:
  250. NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val->value.i);
  251. break;
  252. case SWITCH_TYPE_STRING:
  253. if (!val->value.s)
  254. goto nla_put_failure;
  255. NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val->value.s);
  256. break;
  257. case SWITCH_TYPE_PORTS:
  258. if (send_attr_ports(msg, val) < 0)
  259. goto nla_put_failure;
  260. break;
  261. default:
  262. goto nla_put_failure;
  263. }
  264. return 0;
  265. nla_put_failure:
  266. return -1;
  267. }
  268. int
  269. swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
  270. {
  271. int cmd;
  272. switch(attr->atype) {
  273. case SWLIB_ATTR_GROUP_GLOBAL:
  274. cmd = SWITCH_CMD_SET_GLOBAL;
  275. break;
  276. case SWLIB_ATTR_GROUP_PORT:
  277. cmd = SWITCH_CMD_SET_PORT;
  278. break;
  279. case SWLIB_ATTR_GROUP_VLAN:
  280. cmd = SWITCH_CMD_SET_VLAN;
  281. break;
  282. default:
  283. return -EINVAL;
  284. }
  285. val->attr = attr;
  286. return swlib_call(cmd, NULL, send_attr_val, val);
  287. }
  288. struct attrlist_arg {
  289. int id;
  290. int atype;
  291. struct switch_dev *dev;
  292. struct switch_attr *prev;
  293. struct switch_attr **head;
  294. };
  295. static int
  296. add_id(struct nl_msg *msg, void *arg)
  297. {
  298. struct attrlist_arg *l = arg;
  299. NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id);
  300. return 0;
  301. nla_put_failure:
  302. return -1;
  303. }
  304. static int
  305. add_attr(struct nl_msg *msg, void *ptr)
  306. {
  307. struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
  308. struct attrlist_arg *arg = ptr;
  309. struct switch_attr *new;
  310. if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
  311. genlmsg_attrlen(gnlh, 0), NULL) < 0)
  312. goto done;
  313. new = swlib_alloc(sizeof(struct switch_attr));
  314. if (!new)
  315. goto done;
  316. new->dev = arg->dev;
  317. new->atype = arg->atype;
  318. if (arg->prev) {
  319. arg->prev->next = new;
  320. } else {
  321. arg->prev = *arg->head;
  322. }
  323. *arg->head = new;
  324. arg->head = &new->next;
  325. if (tb[SWITCH_ATTR_OP_ID])
  326. new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]);
  327. if (tb[SWITCH_ATTR_OP_TYPE])
  328. new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]);
  329. if (tb[SWITCH_ATTR_OP_NAME])
  330. new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME]));
  331. if (tb[SWITCH_ATTR_OP_DESCRIPTION])
  332. new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION]));
  333. done:
  334. return NL_SKIP;
  335. }
  336. int
  337. swlib_scan(struct switch_dev *dev)
  338. {
  339. struct attrlist_arg arg;
  340. if (dev->ops || dev->port_ops || dev->vlan_ops)
  341. return 0;
  342. arg.atype = SWLIB_ATTR_GROUP_GLOBAL;
  343. arg.dev = dev;
  344. arg.id = dev->id;
  345. arg.prev = NULL;
  346. arg.head = &dev->ops;
  347. swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg);
  348. arg.atype = SWLIB_ATTR_GROUP_PORT;
  349. arg.prev = NULL;
  350. arg.head = &dev->port_ops;
  351. swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg);
  352. arg.atype = SWLIB_ATTR_GROUP_VLAN;
  353. arg.prev = NULL;
  354. arg.head = &dev->vlan_ops;
  355. swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg);
  356. return 0;
  357. }
  358. struct switch_attr *swlib_lookup_attr(struct switch_dev *dev,
  359. enum swlib_attr_group atype, const char *name)
  360. {
  361. struct switch_attr *head;
  362. if (!name || !dev)
  363. return NULL;
  364. switch(atype) {
  365. case SWLIB_ATTR_GROUP_GLOBAL:
  366. head = dev->ops;
  367. break;
  368. case SWLIB_ATTR_GROUP_PORT:
  369. head = dev->port_ops;
  370. break;
  371. case SWLIB_ATTR_GROUP_VLAN:
  372. head = dev->vlan_ops;
  373. break;
  374. }
  375. while(head) {
  376. if (!strcmp(name, head->name))
  377. return head;
  378. head = head->next;
  379. }
  380. return NULL;
  381. }
  382. static void
  383. swlib_priv_free(void)
  384. {
  385. if (cache)
  386. nl_cache_free(cache);
  387. if (handle)
  388. nl_handle_destroy(handle);
  389. handle = NULL;
  390. cache = NULL;
  391. }
  392. static int
  393. swlib_priv_init(void)
  394. {
  395. handle = nl_handle_alloc();
  396. if (!handle) {
  397. DPRINTF("Failed to create handle\n");
  398. goto err;
  399. }
  400. if (genl_connect(handle)) {
  401. DPRINTF("Failed to connect to generic netlink\n");
  402. goto err;
  403. }
  404. cache = genl_ctrl_alloc_cache(handle);
  405. if (!cache) {
  406. DPRINTF("Failed to allocate netlink cache\n");
  407. goto err;
  408. }
  409. family = genl_ctrl_search_by_name(cache, "switch");
  410. if (!family) {
  411. DPRINTF("Switch API not present\n");
  412. goto err;
  413. }
  414. return 0;
  415. err:
  416. swlib_priv_free();
  417. return -EINVAL;
  418. }
  419. struct swlib_scan_arg {
  420. const char *name;
  421. struct switch_dev *head;
  422. struct switch_dev *ptr;
  423. };
  424. static int
  425. add_switch(struct nl_msg *msg, void *arg)
  426. {
  427. struct swlib_scan_arg *sa = arg;
  428. struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
  429. struct switch_dev *dev;
  430. const char *name;
  431. if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
  432. goto done;
  433. if (!tb[SWITCH_ATTR_DEV_NAME])
  434. goto done;
  435. name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]);
  436. if (sa->name && (strcmp(name, sa->name) != 0))
  437. goto done;
  438. dev = swlib_alloc(sizeof(struct switch_dev));
  439. if (!dev)
  440. goto done;
  441. dev->dev_name = strdup(name);
  442. if (tb[SWITCH_ATTR_ID])
  443. dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]);
  444. if (tb[SWITCH_ATTR_NAME])
  445. dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_DEV_NAME]));
  446. if (tb[SWITCH_ATTR_PORTS])
  447. dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]);
  448. if (tb[SWITCH_ATTR_VLANS])
  449. dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]);
  450. if (!sa->head) {
  451. sa->head = dev;
  452. sa->ptr = dev;
  453. } else {
  454. sa->ptr->next = dev;
  455. sa->ptr = dev;
  456. }
  457. refcount++;
  458. done:
  459. return NL_SKIP;
  460. }
  461. struct switch_dev *
  462. swlib_connect(const char *name)
  463. {
  464. struct swlib_scan_arg arg;
  465. int err;
  466. if (!refcount) {
  467. if (swlib_priv_init() < 0)
  468. return NULL;
  469. };
  470. arg.head = NULL;
  471. arg.ptr = NULL;
  472. arg.name = name;
  473. swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg);
  474. if (!refcount)
  475. swlib_priv_free();
  476. return arg.head;
  477. }
  478. static void
  479. swlib_free_attributes(struct switch_attr **head)
  480. {
  481. struct switch_attr *a = *head;
  482. struct switch_attr *next;
  483. while (a) {
  484. next = a->next;
  485. free(a);
  486. a = next;
  487. }
  488. *head = NULL;
  489. }
  490. void
  491. swlib_free(struct switch_dev *dev)
  492. {
  493. swlib_free_attributes(&dev->ops);
  494. swlib_free_attributes(&dev->port_ops);
  495. swlib_free_attributes(&dev->vlan_ops);
  496. free(dev);
  497. if (--refcount == 0)
  498. swlib_priv_free();
  499. }
  500. void
  501. swlib_free_all(struct switch_dev *dev)
  502. {
  503. struct switch_dev *p;
  504. while (dev) {
  505. p = dev->next;
  506. swlib_free(dev);
  507. dev = p;
  508. }
  509. }