Johannes Berg | 2ef1be6 | 2008-04-02 17:40:11 +0200 | [diff] [blame] | 1 | #include <linux/nl80211.h> |
| 2 | #include <net/if.h> |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 3 | #include <errno.h> |
Johannes Berg | d5ac8ad | 2008-04-03 15:24:13 +0200 | [diff] [blame] | 4 | #include <string.h> |
Johannes Berg | 2ef1be6 | 2008-04-02 17:40:11 +0200 | [diff] [blame] | 5 | |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 6 | #include <netlink/genl/genl.h> |
| 7 | #include <netlink/genl/family.h> |
| 8 | #include <netlink/genl/ctrl.h> |
| 9 | #include <netlink/msg.h> |
| 10 | #include <netlink/attr.h> |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 11 | |
| 12 | #include "iw.h" |
| 13 | |
| 14 | /* return 0 if not found, 1 if ok, -1 on error */ |
| 15 | static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type) |
| 16 | { |
| 17 | char *tpstr; |
| 18 | |
| 19 | if (*argc < 2) |
| 20 | return 0; |
| 21 | |
| 22 | if (strcmp((*argv)[0], "type")) |
| 23 | return 0; |
| 24 | |
| 25 | tpstr = (*argv)[1]; |
| 26 | *argc -= 2; |
| 27 | *argv += 2; |
| 28 | |
| 29 | if (strcmp(tpstr, "adhoc") == 0 || |
| 30 | strcmp(tpstr, "ibss") == 0) { |
| 31 | *type = NL80211_IFTYPE_ADHOC; |
| 32 | return 1; |
| 33 | } else if (strcmp(tpstr, "monitor") == 0) { |
| 34 | *type = NL80211_IFTYPE_MONITOR; |
| 35 | return 1; |
Johannes Berg | 4d3a72d | 2008-04-14 15:41:31 +0200 | [diff] [blame] | 36 | } else if (strcmp(tpstr, "__ap") == 0) { |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 37 | *type = NL80211_IFTYPE_AP; |
| 38 | return 1; |
Johannes Berg | 4d3a72d | 2008-04-14 15:41:31 +0200 | [diff] [blame] | 39 | } else if (strcmp(tpstr, "__ap_vlan") == 0) { |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 40 | *type = NL80211_IFTYPE_AP_VLAN; |
| 41 | return 1; |
| 42 | } else if (strcmp(tpstr, "wds") == 0) { |
| 43 | *type = NL80211_IFTYPE_WDS; |
| 44 | return 1; |
| 45 | } else if (strcmp(tpstr, "station") == 0) { |
| 46 | *type = NL80211_IFTYPE_STATION; |
| 47 | return 1; |
Luis Carlos Cobo | 3d1e870 | 2008-04-01 12:03:41 -0700 | [diff] [blame] | 48 | } else if (strcmp(tpstr, "mp") == 0 || |
| 49 | strcmp(tpstr, "mesh") == 0) { |
| 50 | *type = NL80211_IFTYPE_MESH_POINT; |
| 51 | return 1; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | |
| 55 | fprintf(stderr, "invalid interface type %s\n", tpstr); |
| 56 | return -1; |
| 57 | } |
| 58 | |
| 59 | static int handle_interface_add(struct nl80211_state *state, |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 60 | struct nl_msg *msg, |
| 61 | int argc, char **argv) |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 62 | { |
Andrew Lutomirski | 2dfd6bf | 2007-12-20 18:50:07 +0100 | [diff] [blame] | 63 | char *name; |
Luis Carlos Cobo | 3d1e870 | 2008-04-01 12:03:41 -0700 | [diff] [blame] | 64 | char *mesh_id = NULL; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 65 | enum nl80211_iftype type; |
Johannes Berg | 23dfe29 | 2008-04-03 15:39:27 +0200 | [diff] [blame] | 66 | int tpset, err = -ENOBUFS; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 67 | |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 68 | if (argc < 1) |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 69 | return -1; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 70 | |
Andrew Lutomirski | 2dfd6bf | 2007-12-20 18:50:07 +0100 | [diff] [blame] | 71 | name = argv[0]; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 72 | argc--; |
| 73 | argv++; |
| 74 | |
Andrew Lutomirski | 2dfd6bf | 2007-12-20 18:50:07 +0100 | [diff] [blame] | 75 | tpset = get_if_type(&argc, &argv, &type); |
| 76 | if (tpset == 0) |
| 77 | fprintf(stderr, "you must specify an interface type\n"); |
| 78 | if (tpset <= 0) |
| 79 | return -1; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 80 | |
| 81 | if (argc) { |
Luis Carlos Cobo | 3d1e870 | 2008-04-01 12:03:41 -0700 | [diff] [blame] | 82 | if (strcmp(argv[0], "mesh_id") != 0) { |
| 83 | fprintf(stderr, "option %s not supported\n", argv[0]); |
| 84 | return -1; |
| 85 | } |
| 86 | argc--; |
| 87 | argv++; |
| 88 | |
| 89 | if (!argc) { |
| 90 | fprintf(stderr, "not enough arguments\n"); |
| 91 | return -1; |
| 92 | } |
| 93 | mesh_id = argv[0]; |
| 94 | argc--; |
| 95 | argv++; |
| 96 | } |
| 97 | |
| 98 | if (argc) { |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 99 | fprintf(stderr, "too many arguments\n"); |
| 100 | return -1; |
| 101 | } |
| 102 | |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 103 | NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name); |
| 104 | if (tpset) |
| 105 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type); |
Luis Carlos Cobo | 3d1e870 | 2008-04-01 12:03:41 -0700 | [diff] [blame] | 106 | if (mesh_id) |
| 107 | NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(mesh_id), mesh_id); |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 108 | |
| 109 | if ((err = nl_send_auto_complete(state->nl_handle, msg)) < 0 || |
| 110 | (err = nl_wait_for_ack(state->nl_handle)) < 0) { |
| 111 | nla_put_failure: |
| 112 | fprintf(stderr, "failed to create interface: %d\n", err); |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 113 | return 1; |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 114 | } |
| 115 | |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 116 | return 0; |
| 117 | } |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 118 | COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>]", |
| 119 | NL80211_CMD_NEW_INTERFACE, 0, CIB_PHY, handle_interface_add); |
Johannes Berg | 45c7212 | 2007-09-28 23:47:38 +0200 | [diff] [blame] | 120 | |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 121 | static int handle_interface_del(struct nl80211_state *state, |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 122 | struct nl_msg *msg, |
| 123 | int argc, char **argv) |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 124 | { |
Johannes Berg | 23dfe29 | 2008-04-03 15:39:27 +0200 | [diff] [blame] | 125 | int err = -ENOBUFS; |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 126 | |
| 127 | if ((err = nl_send_auto_complete(state->nl_handle, msg)) < 0 || |
| 128 | (err = nl_wait_for_ack(state->nl_handle)) < 0) { |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 129 | fprintf(stderr, "failed to remove interface: %d\n", err); |
| 130 | nlmsg_free(msg); |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 131 | return 1; |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 132 | } |
| 133 | |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 134 | return 0; |
| 135 | } |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 136 | TOPLEVEL(del, NULL, NL80211_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del); |
Johannes Berg | 3fcfe40 | 2007-09-28 23:50:25 +0200 | [diff] [blame] | 137 | |
Johannes Berg | 541ef42 | 2008-09-16 14:50:11 +0200 | [diff] [blame] | 138 | static int print_iface_handler(struct nl_msg *msg, void *arg) |
| 139 | { |
| 140 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| 141 | struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; |
| 142 | |
| 143 | nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| 144 | genlmsg_attrlen(gnlh, 0), NULL); |
| 145 | |
| 146 | if (tb_msg[NL80211_ATTR_IFNAME]) |
| 147 | printf("Interface %s\n", nla_get_string(tb_msg[NL80211_ATTR_IFNAME])); |
| 148 | if (tb_msg[NL80211_ATTR_IFINDEX]) |
| 149 | printf("\tifindex %d\n", nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX])); |
| 150 | if (tb_msg[NL80211_ATTR_IFTYPE]) |
| 151 | printf("\ttype %s\n", iftype_name(nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]))); |
| 152 | |
| 153 | return NL_SKIP; |
| 154 | } |
| 155 | |
| 156 | static int ack_wait_handler(struct nl_msg *msg, void *arg) |
| 157 | { |
| 158 | int *finished = arg; |
| 159 | |
| 160 | *finished = 1; |
| 161 | return NL_STOP; |
| 162 | } |
| 163 | |
| 164 | static int handle_interface_info(struct nl80211_state *state, |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 165 | struct nl_msg *msg, |
| 166 | int argc, char **argv) |
Johannes Berg | 541ef42 | 2008-09-16 14:50:11 +0200 | [diff] [blame] | 167 | { |
| 168 | int err = -ENOBUFS; |
Johannes Berg | 541ef42 | 2008-09-16 14:50:11 +0200 | [diff] [blame] | 169 | struct nl_cb *cb = NULL; |
| 170 | int finished = 0; |
| 171 | |
Johannes Berg | 541ef42 | 2008-09-16 14:50:11 +0200 | [diff] [blame] | 172 | cb = nl_cb_alloc(NL_CB_CUSTOM); |
| 173 | if (!cb) |
| 174 | goto out; |
| 175 | |
| 176 | if (nl_send_auto_complete(state->nl_handle, msg) < 0) |
| 177 | goto out; |
| 178 | |
| 179 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_iface_handler, NULL); |
| 180 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); |
| 181 | |
| 182 | err = nl_recvmsgs(state->nl_handle, cb); |
| 183 | |
| 184 | if (!finished) |
| 185 | err = nl_wait_for_ack(state->nl_handle); |
| 186 | |
| 187 | if (err) |
| 188 | fprintf(stderr, "failed to get information: %d\n", err); |
| 189 | |
| 190 | out: |
Johannes Berg | 541ef42 | 2008-09-16 14:50:11 +0200 | [diff] [blame] | 191 | nl_cb_put(cb); |
| 192 | return 0; |
| 193 | } |
Johannes Berg | bd396f2 | 2008-09-16 16:56:09 +0200 | [diff] [blame^] | 194 | TOPLEVEL(info, NULL, NL80211_CMD_GET_INTERFACE, 0, CIB_NETDEV, handle_interface_info); |