| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #include <stdio.h> |
| 3 | #include <string.h> |
| 4 | |
| 5 | #include <ynl.h> |
| 6 | |
| 7 | #include <net/if.h> |
| 8 | |
| 9 | #include "netdev-user.h" |
| 10 | |
| 11 | /* netdev genetlink family code sample |
| 12 | * This sample shows off basics of the netdev family but also notification |
| 13 | * handling, hence the somewhat odd UI. We subscribe to notifications first |
| 14 | * then wait for ifc selection, so the socket may already accumulate |
| 15 | * notifications as we wait. This allows us to test that YNL can handle |
| 16 | * requests and notifications getting interleaved. |
| 17 | */ |
| 18 | |
| 19 | static void netdev_print_device(struct netdev_dev_get_rsp *d, unsigned int op) |
| 20 | { |
| 21 | char ifname[IF_NAMESIZE]; |
| 22 | const char *name; |
| 23 | |
| 24 | if (!d->_present.ifindex) |
| 25 | return; |
| 26 | |
| 27 | name = if_indextoname(d->ifindex, ifname); |
| 28 | if (name) |
| 29 | printf("%8s" , name); |
| 30 | printf("[%d]\t" , d->ifindex); |
| 31 | |
| 32 | if (!d->_present.xdp_features) |
| 33 | return; |
| 34 | |
| 35 | printf("xdp-features (%llx):" , d->xdp_features); |
| 36 | for (int i = 0; d->xdp_features >= 1U << i; i++) { |
| 37 | if (d->xdp_features & (1U << i)) |
| 38 | printf(" %s" , netdev_xdp_act_str(1 << i)); |
| 39 | } |
| 40 | |
| 41 | printf(" xdp-rx-metadata-features (%llx):" , d->xdp_rx_metadata_features); |
| 42 | for (int i = 0; d->xdp_rx_metadata_features >= 1U << i; i++) { |
| 43 | if (d->xdp_rx_metadata_features & (1U << i)) |
| 44 | printf(" %s" , netdev_xdp_rx_metadata_str(1 << i)); |
| 45 | } |
| 46 | |
| 47 | printf(" xsk-features (%llx):" , d->xsk_features); |
| 48 | for (int i = 0; d->xsk_features >= 1U << i; i++) { |
| 49 | if (d->xsk_features & (1U << i)) |
| 50 | printf(" %s" , netdev_xsk_flags_str(1 << i)); |
| 51 | } |
| 52 | |
| 53 | printf(" xdp-zc-max-segs=%u" , d->xdp_zc_max_segs); |
| 54 | |
| 55 | name = netdev_op_str(op); |
| 56 | if (name) |
| 57 | printf(" (ntf: %s)" , name); |
| 58 | printf("\n" ); |
| 59 | } |
| 60 | |
| 61 | int main(int argc, char **argv) |
| 62 | { |
| 63 | struct netdev_dev_get_list *devs; |
| 64 | struct ynl_ntf_base_type *ntf; |
| 65 | struct ynl_error yerr; |
| 66 | struct ynl_sock *ys; |
| 67 | int ifindex = 0; |
| 68 | |
| 69 | if (argc > 1) |
| 70 | ifindex = strtol(argv[1], NULL, 0); |
| 71 | |
| 72 | ys = ynl_sock_create(&ynl_netdev_family, &yerr); |
| 73 | if (!ys) { |
| 74 | fprintf(stderr, "YNL: %s\n" , yerr.msg); |
| 75 | return 1; |
| 76 | } |
| 77 | |
| 78 | if (ynl_subscribe(ys, "mgmt" )) |
| 79 | goto err_close; |
| 80 | |
| 81 | printf("Select ifc ($ifindex; or 0 = dump; or -2 ntf check): " ); |
| 82 | if (scanf("%d" , &ifindex) != 1) { |
| 83 | fprintf(stderr, "Error: unable to parse input\n" ); |
| 84 | goto err_destroy; |
| 85 | } |
| 86 | |
| 87 | if (ifindex > 0) { |
| 88 | struct netdev_dev_get_req *req; |
| 89 | struct netdev_dev_get_rsp *d; |
| 90 | |
| 91 | req = netdev_dev_get_req_alloc(); |
| 92 | netdev_dev_get_req_set_ifindex(req, ifindex); |
| 93 | |
| 94 | d = netdev_dev_get(ys, req); |
| 95 | netdev_dev_get_req_free(req); |
| 96 | if (!d) |
| 97 | goto err_close; |
| 98 | |
| 99 | netdev_print_device(d, op: 0); |
| 100 | netdev_dev_get_rsp_free(d); |
| 101 | } else if (!ifindex) { |
| 102 | devs = netdev_dev_get_dump(ys); |
| 103 | if (!devs) |
| 104 | goto err_close; |
| 105 | |
| 106 | if (ynl_dump_empty(devs)) |
| 107 | fprintf(stderr, "Error: no devices reported\n" ); |
| 108 | ynl_dump_foreach(devs, d) |
| 109 | netdev_print_device(d: d, op: 0); |
| 110 | netdev_dev_get_list_free(devs); |
| 111 | } else if (ifindex == -2) { |
| 112 | ynl_ntf_check(ys); |
| 113 | } |
| 114 | while ((ntf = ynl_ntf_dequeue(ys))) { |
| 115 | netdev_print_device(d: (struct netdev_dev_get_rsp *)&ntf->data, |
| 116 | op: ntf->cmd); |
| 117 | ynl_ntf_free(ntf); |
| 118 | } |
| 119 | |
| 120 | ynl_sock_destroy(ys); |
| 121 | return 0; |
| 122 | |
| 123 | err_close: |
| 124 | fprintf(stderr, "YNL: %s\n" , ys->err.msg); |
| 125 | err_destroy: |
| 126 | ynl_sock_destroy(ys); |
| 127 | return 2; |
| 128 | } |
| 129 | |