colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 1 | #include <net/if.h> |
| 2 | #include <errno.h> |
| 3 | #include <string.h> |
| 4 | |
| 5 | #include <netlink/genl/genl.h> |
| 6 | #include <netlink/genl/family.h> |
| 7 | #include <netlink/genl/ctrl.h> |
| 8 | #include <netlink/msg.h> |
| 9 | #include <netlink/attr.h> |
| 10 | |
| 11 | #include "nl80211.h" |
| 12 | #include "iw.h" |
| 13 | |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 14 | SECTION(mesh); |
| 15 | |
| 16 | |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 17 | typedef struct _any_t { |
| 18 | union { |
| 19 | uint32_t as_32; |
Ashok Nagarajan | be97e51 | 2012-04-18 11:00:44 -0700 | [diff] [blame] | 20 | int32_t as_s32; |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 21 | uint16_t as_16; |
| 22 | uint8_t as_8; |
| 23 | } u; |
| 24 | } _any; |
| 25 | |
| 26 | /* describes a mesh parameter */ |
| 27 | struct mesh_param_descr { |
| 28 | const char *name; |
| 29 | enum nl80211_meshconf_params mesh_param_num; |
| 30 | int (*nla_put_fn)(struct nl_msg*, int, _any*); |
| 31 | uint32_t (*parse_fn)(const char*, _any*); |
| 32 | void (*nla_print_fn)(struct nlattr *); |
| 33 | }; |
| 34 | |
| 35 | /* utility functions for manipulating and printing u8/u16/u32 values and |
| 36 | * timesouts. */ |
| 37 | static int _my_nla_put_u8(struct nl_msg *n, int mesh_param_num, _any *value) |
| 38 | { |
| 39 | return nla_put(n, mesh_param_num, sizeof(uint8_t), &value->u.as_8); |
| 40 | } |
| 41 | |
| 42 | static int _my_nla_put_u16(struct nl_msg *n, int mesh_param_num, _any *value) |
| 43 | { |
| 44 | return nla_put(n, mesh_param_num, sizeof(uint16_t), &value->u.as_16); |
| 45 | } |
| 46 | |
| 47 | static int _my_nla_put_u32(struct nl_msg *n, int mesh_param_num, _any *value) |
| 48 | { |
| 49 | return nla_put(n, mesh_param_num, sizeof(uint32_t), &value->u.as_32); |
| 50 | } |
| 51 | |
| 52 | static uint32_t _parse_u8(const char *str, _any *ret) |
| 53 | { |
| 54 | char *endptr = NULL; |
| 55 | unsigned long int v = strtoul(str, &endptr, 10); |
| 56 | if (*endptr != '\0') |
| 57 | return 0xff; |
| 58 | if (v > 0xff) |
| 59 | return 0xff; |
| 60 | ret->u.as_8 = (uint8_t)v; |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | static uint32_t _parse_u8_as_bool(const char *str, _any *ret) |
| 65 | { |
| 66 | char *endptr = NULL; |
| 67 | unsigned long int v = strtoul(str, &endptr, 10); |
| 68 | if (*endptr != '\0') |
| 69 | return 0x1; |
| 70 | if (v > 0x1) |
| 71 | return 0x1; |
| 72 | ret->u.as_8 = (uint8_t)v; |
| 73 | return 0; |
| 74 | } |
| 75 | |
| 76 | static uint32_t _parse_u16(const char *str, _any *ret) |
| 77 | { |
| 78 | char *endptr = NULL; |
| 79 | long int v = strtol(str, &endptr, 10); |
| 80 | if (*endptr != '\0') |
| 81 | return 0xffff; |
| 82 | if ((v < 0) || (v > 0xffff)) |
| 83 | return 0xffff; |
| 84 | ret->u.as_16 = (uint16_t)v; |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | static uint32_t _parse_u32(const char *str, _any *ret) |
| 89 | { |
| 90 | char *endptr = NULL; |
| 91 | long long int v = strtoll(str, &endptr, 10); |
| 92 | if (*endptr != '\0') |
| 93 | return 0xffffffff; |
| 94 | if ((v < 0) || (v > 0xffffffff)) |
| 95 | return 0xffffffff; |
| 96 | ret->u.as_32 = (uint32_t)v; |
| 97 | return 0; |
| 98 | } |
| 99 | |
Ashok Nagarajan | be97e51 | 2012-04-18 11:00:44 -0700 | [diff] [blame] | 100 | static uint32_t _parse_s32(const char *str, _any *ret) |
| 101 | { |
| 102 | char *endptr = NULL; |
| 103 | long int v = strtol(str, &endptr, 10); |
| 104 | if (*endptr != '\0') |
| 105 | return 0xffffffff; |
| 106 | if (v > 0xff) |
| 107 | return 0xffffffff; |
| 108 | ret->u.as_s32 = (int32_t)v; |
| 109 | return 0; |
| 110 | } |
| 111 | |
Marco Porsch | 46758fa | 2013-01-18 13:05:31 +0100 | [diff] [blame] | 112 | static uint32_t _parse_u32_power_mode(const char *str, _any *ret) |
| 113 | { |
| 114 | unsigned long int v; |
| 115 | |
| 116 | /* Parse attribute for the name of power mode */ |
| 117 | if (!strcmp(str, "active")) |
| 118 | v = NL80211_MESH_POWER_ACTIVE; |
| 119 | else if (!strcmp(str, "light")) |
| 120 | v = NL80211_MESH_POWER_LIGHT_SLEEP; |
| 121 | else if (!strcmp(str, "deep")) |
| 122 | v = NL80211_MESH_POWER_DEEP_SLEEP; |
| 123 | else |
| 124 | return 0xff; |
| 125 | |
| 126 | ret->u.as_32 = (uint32_t)v; |
| 127 | return 0; |
| 128 | } |
Ashok Nagarajan | be97e51 | 2012-04-18 11:00:44 -0700 | [diff] [blame] | 129 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 130 | static void _print_u8(struct nlattr *a) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 131 | { |
| 132 | printf("%d", nla_get_u8(a)); |
| 133 | } |
| 134 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 135 | static void _print_u16(struct nlattr *a) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 136 | { |
| 137 | printf("%d", nla_get_u16(a)); |
| 138 | } |
| 139 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 140 | static void _print_u16_timeout(struct nlattr *a) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 141 | { |
| 142 | printf("%d milliseconds", nla_get_u16(a)); |
| 143 | } |
| 144 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 145 | static void _print_u16_in_TUs(struct nlattr *a) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 146 | { |
| 147 | printf("%d TUs", nla_get_u16(a)); |
| 148 | } |
| 149 | |
Ashok Nagarajan | d9f730c | 2012-04-12 17:45:12 -0700 | [diff] [blame] | 150 | static void _print_u32(struct nlattr *a) |
| 151 | { |
| 152 | printf("%d", nla_get_u32(a)); |
| 153 | } |
| 154 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 155 | static void _print_u32_timeout(struct nlattr *a) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 156 | { |
| 157 | printf("%u milliseconds", nla_get_u32(a)); |
| 158 | } |
| 159 | |
Colleen Twitty | 770f80c | 2013-07-08 10:04:18 -0700 | [diff] [blame] | 160 | static void _print_u32_in_seconds(struct nlattr *a) |
| 161 | { |
| 162 | printf("%d seconds", nla_get_u32(a)); |
| 163 | } |
| 164 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 165 | static void _print_u32_in_TUs(struct nlattr *a) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 166 | { |
| 167 | printf("%d TUs", nla_get_u32(a)); |
| 168 | } |
| 169 | |
Marco Porsch | 46758fa | 2013-01-18 13:05:31 +0100 | [diff] [blame] | 170 | static void _print_u32_power_mode(struct nlattr *a) |
| 171 | { |
| 172 | unsigned long v = nla_get_u32(a); |
| 173 | |
| 174 | switch (v) { |
| 175 | case NL80211_MESH_POWER_ACTIVE: |
| 176 | printf("active"); |
| 177 | break; |
| 178 | case NL80211_MESH_POWER_LIGHT_SLEEP: |
| 179 | printf("light"); |
| 180 | break; |
| 181 | case NL80211_MESH_POWER_DEEP_SLEEP: |
| 182 | printf("deep"); |
| 183 | break; |
| 184 | default: |
| 185 | printf("undefined"); |
| 186 | break; |
| 187 | } |
| 188 | } |
| 189 | |
Ashok Nagarajan | be97e51 | 2012-04-18 11:00:44 -0700 | [diff] [blame] | 190 | static void _print_s32_in_dBm(struct nlattr *a) |
| 191 | { |
| 192 | printf("%d dBm", (int32_t) nla_get_u32(a)); |
| 193 | } |
| 194 | |
| 195 | |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 196 | /* The current mesh parameters */ |
| 197 | const static struct mesh_param_descr _mesh_param_descrs[] = |
| 198 | { |
| 199 | {"mesh_retry_timeout", |
| 200 | NL80211_MESHCONF_RETRY_TIMEOUT, |
| 201 | _my_nla_put_u16, _parse_u16, _print_u16_timeout}, |
| 202 | {"mesh_confirm_timeout", |
| 203 | NL80211_MESHCONF_CONFIRM_TIMEOUT, |
| 204 | _my_nla_put_u16, _parse_u16, _print_u16_timeout}, |
| 205 | {"mesh_holding_timeout", |
| 206 | NL80211_MESHCONF_HOLDING_TIMEOUT, |
| 207 | _my_nla_put_u16, _parse_u16, _print_u16_timeout}, |
| 208 | {"mesh_max_peer_links", |
| 209 | NL80211_MESHCONF_MAX_PEER_LINKS, |
| 210 | _my_nla_put_u16, _parse_u16, _print_u16}, |
| 211 | {"mesh_max_retries", |
| 212 | NL80211_MESHCONF_MAX_RETRIES, |
| 213 | _my_nla_put_u8, _parse_u8, _print_u8}, |
| 214 | {"mesh_ttl", |
| 215 | NL80211_MESHCONF_TTL, |
| 216 | _my_nla_put_u8, _parse_u8, _print_u8}, |
Johannes Berg | 247beb9 | 2010-12-03 09:18:42 +0100 | [diff] [blame] | 217 | {"mesh_element_ttl", |
| 218 | NL80211_MESHCONF_ELEMENT_TTL, |
| 219 | _my_nla_put_u8, _parse_u8, _print_u8}, |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 220 | {"mesh_auto_open_plinks", |
| 221 | NL80211_MESHCONF_AUTO_OPEN_PLINKS, |
| 222 | _my_nla_put_u8, _parse_u8_as_bool, _print_u8}, |
| 223 | {"mesh_hwmp_max_preq_retries", |
| 224 | NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, |
| 225 | _my_nla_put_u8, _parse_u8, _print_u8}, |
| 226 | {"mesh_path_refresh_time", |
| 227 | NL80211_MESHCONF_PATH_REFRESH_TIME, |
| 228 | _my_nla_put_u32, _parse_u32, _print_u32_timeout}, |
| 229 | {"mesh_min_discovery_timeout", |
| 230 | NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, |
| 231 | _my_nla_put_u16, _parse_u16, _print_u16_timeout}, |
| 232 | {"mesh_hwmp_active_path_timeout", |
| 233 | NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, |
| 234 | _my_nla_put_u32, _parse_u32, _print_u32_in_TUs}, |
| 235 | {"mesh_hwmp_preq_min_interval", |
| 236 | NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, |
| 237 | _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, |
| 238 | {"mesh_hwmp_net_diameter_traversal_time", |
| 239 | NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
| 240 | _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, |
Rui Paulo | cc37218 | 2009-11-09 12:53:07 +0000 | [diff] [blame] | 241 | {"mesh_hwmp_rootmode", NL80211_MESHCONF_HWMP_ROOTMODE, |
| 242 | _my_nla_put_u8, _parse_u8, _print_u8}, |
Javier Cardona | b9aa711 | 2011-08-09 17:03:30 -0700 | [diff] [blame] | 243 | {"mesh_hwmp_rann_interval", NL80211_MESHCONF_HWMP_RANN_INTERVAL, |
Chun-Yeow Yeoh | b1b5713 | 2012-07-05 00:26:33 +0800 | [diff] [blame] | 244 | _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, |
Javier Cardona | b9aa711 | 2011-08-09 17:03:30 -0700 | [diff] [blame] | 245 | {"mesh_gate_announcements", NL80211_MESHCONF_GATE_ANNOUNCEMENTS, |
| 246 | _my_nla_put_u8, _parse_u8, _print_u8}, |
Chun-Yeow Yeoh | accd0d2 | 2012-03-05 01:41:46 +0800 | [diff] [blame] | 247 | {"mesh_fwding", NL80211_MESHCONF_FORWARDING, |
| 248 | _my_nla_put_u8, _parse_u8_as_bool, _print_u8}, |
Ashok Nagarajan | d9f730c | 2012-04-12 17:45:12 -0700 | [diff] [blame] | 249 | {"mesh_sync_offset_max_neighor", |
| 250 | NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, |
| 251 | _my_nla_put_u32, _parse_u32, _print_u32}, |
Ashok Nagarajan | be97e51 | 2012-04-18 11:00:44 -0700 | [diff] [blame] | 252 | {"mesh_rssi_threshold", NL80211_MESHCONF_RSSI_THRESHOLD, |
| 253 | _my_nla_put_u32, _parse_s32, _print_s32_in_dBm}, |
Chun-Yeow Yeoh | b1b5713 | 2012-07-05 00:26:33 +0800 | [diff] [blame] | 254 | {"mesh_hwmp_active_path_to_root_timeout", |
| 255 | NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, |
| 256 | _my_nla_put_u32, _parse_u32, _print_u32_in_TUs}, |
| 257 | {"mesh_hwmp_root_interval", NL80211_MESHCONF_HWMP_ROOT_INTERVAL, |
| 258 | _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, |
| 259 | {"mesh_hwmp_confirmation_interval", |
| 260 | NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, |
| 261 | _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, |
Marco Porsch | 46758fa | 2013-01-18 13:05:31 +0100 | [diff] [blame] | 262 | {"mesh_power_mode", NL80211_MESHCONF_POWER_MODE, |
| 263 | _my_nla_put_u32, _parse_u32_power_mode, _print_u32_power_mode}, |
| 264 | {"mesh_awake_window", NL80211_MESHCONF_AWAKE_WINDOW, |
| 265 | _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, |
Colleen Twitty | 770f80c | 2013-07-08 10:04:18 -0700 | [diff] [blame] | 266 | {"mesh_plink_timeout", NL80211_MESHCONF_PLINK_TIMEOUT, |
| 267 | _my_nla_put_u32, _parse_u32, _print_u32_in_seconds}, |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 268 | }; |
| 269 | |
| 270 | static void print_all_mesh_param_descr(void) |
| 271 | { |
| 272 | int i; |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 273 | |
Johannes Berg | f089471 | 2009-01-29 14:56:08 +0100 | [diff] [blame] | 274 | printf("Possible mesh parameters are:\n"); |
| 275 | |
| 276 | for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) |
| 277 | printf(" - %s\n", _mesh_param_descrs[i].name); |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 278 | } |
| 279 | |
Johannes Berg | a34f62c | 2010-12-02 08:53:04 +0100 | [diff] [blame] | 280 | static const struct mesh_param_descr *find_mesh_param(const char *name) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 281 | { |
| 282 | int i; |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 283 | |
| 284 | /* Find out what mesh parameter we want to change. */ |
Colin McCabe | 4d0d2ea | 2009-01-09 17:13:16 -0800 | [diff] [blame] | 285 | for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) { |
Ashok Nagarajan | 83cb979 | 2012-10-09 13:29:26 -0700 | [diff] [blame] | 286 | if (strcmp(_mesh_param_descrs[i].name, name) == 0) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 287 | return _mesh_param_descrs + i; |
Colin McCabe | 4d0d2ea | 2009-01-09 17:13:16 -0800 | [diff] [blame] | 288 | } |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 289 | |
Ashok Nagarajan | 83cb979 | 2012-10-09 13:29:26 -0700 | [diff] [blame] | 290 | print_all_mesh_param_descr(); |
| 291 | return NULL; |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | /* Setter */ |
Johannes Berg | 7c37a24 | 2009-04-08 13:13:28 +0200 | [diff] [blame] | 295 | static int set_interface_meshparam(struct nl80211_state *state, |
| 296 | struct nl_cb *cb, |
| 297 | struct nl_msg *msg, |
Johannes Berg | 05514f9 | 2012-07-19 11:50:50 +0200 | [diff] [blame] | 298 | int argc, char **argv, |
| 299 | enum id_input id) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 300 | { |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 301 | const struct mesh_param_descr *mdescr; |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 302 | struct nlattr *container; |
Johannes Berg | a34f62c | 2010-12-02 08:53:04 +0100 | [diff] [blame] | 303 | uint32_t ret; |
| 304 | int err; |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 305 | |
Johannes Berg | 656aa24 | 2008-12-08 18:30:22 +0100 | [diff] [blame] | 306 | container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS); |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 307 | if (!container) |
| 308 | return -ENOBUFS; |
Johannes Berg | a34f62c | 2010-12-02 08:53:04 +0100 | [diff] [blame] | 309 | |
| 310 | if (!argc) |
| 311 | return 1; |
| 312 | |
| 313 | while (argc) { |
| 314 | const char *name; |
| 315 | char *value; |
| 316 | _any any; |
| 317 | |
| 318 | memset(&any, 0, sizeof(_any)); |
| 319 | |
| 320 | name = argv[0]; |
| 321 | value = strchr(name, '='); |
| 322 | if (value) { |
| 323 | *value = '\0'; |
| 324 | value++; |
| 325 | argc--; |
| 326 | argv++; |
| 327 | } else { |
| 328 | /* backward compat -- accept w/o '=' */ |
| 329 | if (argc < 2) { |
| 330 | printf("Must specify a value for %s.\n", name); |
| 331 | return 2; |
| 332 | } |
| 333 | value = argv[1]; |
| 334 | argc -= 2; |
| 335 | argv += 2; |
| 336 | } |
| 337 | |
| 338 | mdescr = find_mesh_param(name); |
| 339 | if (!mdescr) |
| 340 | return 2; |
| 341 | |
| 342 | /* Parse the new value */ |
| 343 | ret = mdescr->parse_fn(value, &any); |
| 344 | if (ret != 0) { |
Marco Porsch | 46758fa | 2013-01-18 13:05:31 +0100 | [diff] [blame] | 345 | if (mdescr->mesh_param_num |
| 346 | == NL80211_MESHCONF_POWER_MODE) |
| 347 | printf("%s must be set to active, light or " |
| 348 | "deep.\n", mdescr->name); |
| 349 | else |
| 350 | printf("%s must be set to a number " |
| 351 | "between 0 and %u\n", |
| 352 | mdescr->name, ret); |
| 353 | |
Johannes Berg | a34f62c | 2010-12-02 08:53:04 +0100 | [diff] [blame] | 354 | return 2; |
| 355 | } |
| 356 | |
| 357 | err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any); |
| 358 | if (err) |
| 359 | return err; |
| 360 | } |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 361 | nla_nest_end(msg, container); |
| 362 | |
| 363 | return err; |
| 364 | } |
| 365 | |
Johannes Berg | a34f62c | 2010-12-02 08:53:04 +0100 | [diff] [blame] | 366 | COMMAND(set, mesh_param, "<param>=<value> [<param>=<value>]*", |
Johannes Berg | 70cf454 | 2009-05-05 15:24:39 +0200 | [diff] [blame] | 367 | NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam, |
| 368 | "Set mesh parameter (run command without any to see available ones)."); |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 369 | |
| 370 | /* Getter */ |
| 371 | static int print_mesh_param_handler(struct nl_msg *msg, void *arg) |
| 372 | { |
| 373 | const struct mesh_param_descr *mdescr = arg; |
| 374 | struct nlattr *attrs[NL80211_ATTR_MAX + 1]; |
| 375 | struct nlattr *parent_attr; |
| 376 | struct nlattr *mesh_params[NL80211_MESHCONF_ATTR_MAX + 1]; |
| 377 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| 378 | |
| 379 | /* locate NL80211_ATTR_MESH_PARAMS */ |
| 380 | nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| 381 | genlmsg_attrlen(gnlh, 0), NULL); |
| 382 | parent_attr = attrs[NL80211_ATTR_MESH_PARAMS]; |
| 383 | if (!parent_attr) |
| 384 | return -EINVAL; |
| 385 | |
| 386 | /* unpack the mesh parameters */ |
| 387 | if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX, |
Johannes Berg | 97e2137 | 2010-12-02 08:44:07 +0100 | [diff] [blame] | 388 | parent_attr, NULL)) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 389 | return -EINVAL; |
| 390 | |
Johannes Berg | 97e2137 | 2010-12-02 08:44:07 +0100 | [diff] [blame] | 391 | if (!mdescr) { |
| 392 | int i; |
| 393 | |
| 394 | for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) { |
| 395 | mdescr = &_mesh_param_descrs[i]; |
| 396 | printf("%s = ", mdescr->name); |
| 397 | mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]); |
| 398 | printf("\n"); |
| 399 | } |
| 400 | return NL_SKIP; |
| 401 | } |
| 402 | |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 403 | /* print out the mesh parameter */ |
| 404 | mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]); |
| 405 | printf("\n"); |
| 406 | return NL_SKIP; |
| 407 | } |
| 408 | |
Johannes Berg | 7c37a24 | 2009-04-08 13:13:28 +0200 | [diff] [blame] | 409 | static int get_interface_meshparam(struct nl80211_state *state, |
| 410 | struct nl_cb *cb, |
| 411 | struct nl_msg *msg, |
Johannes Berg | 05514f9 | 2012-07-19 11:50:50 +0200 | [diff] [blame] | 412 | int argc, char **argv, |
| 413 | enum id_input id) |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 414 | { |
Johannes Berg | 97e2137 | 2010-12-02 08:44:07 +0100 | [diff] [blame] | 415 | const struct mesh_param_descr *mdescr = NULL; |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 416 | |
Johannes Berg | 97e2137 | 2010-12-02 08:44:07 +0100 | [diff] [blame] | 417 | if (argc > 1) |
| 418 | return 1; |
| 419 | |
| 420 | if (argc == 1) { |
Johannes Berg | a34f62c | 2010-12-02 08:53:04 +0100 | [diff] [blame] | 421 | mdescr = find_mesh_param(argv[0]); |
Johannes Berg | 97e2137 | 2010-12-02 08:44:07 +0100 | [diff] [blame] | 422 | if (!mdescr) |
| 423 | return 2; |
| 424 | } |
colin@cozybit.com | 46c1ad1 | 2008-10-22 09:55:20 +0200 | [diff] [blame] | 425 | |
| 426 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, |
| 427 | print_mesh_param_handler, (void *)mdescr); |
| 428 | return 0; |
| 429 | } |
| 430 | |
Johannes Berg | 97e2137 | 2010-12-02 08:44:07 +0100 | [diff] [blame] | 431 | COMMAND(get, mesh_param, "[<param>]", |
Johannes Berg | 70cf454 | 2009-05-05 15:24:39 +0200 | [diff] [blame] | 432 | NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam, |
| 433 | "Retrieve mesh parameter (run command without any to see available ones)."); |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 434 | |
| 435 | static int join_mesh(struct nl80211_state *state, struct nl_cb *cb, |
Johannes Berg | 05514f9 | 2012-07-19 11:50:50 +0200 | [diff] [blame] | 436 | struct nl_msg *msg, int argc, char **argv, |
| 437 | enum id_input id) |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 438 | { |
Ashok Nagarajan | 9560e43 | 2012-04-12 17:45:13 -0700 | [diff] [blame] | 439 | struct nlattr *container; |
Chun-Yeow Yeoh | 7d01a09 | 2011-12-08 10:25:15 -0800 | [diff] [blame] | 440 | float rate; |
Chun-Yeow Yeoh | c8473cb | 2014-09-23 09:14:49 +0800 | [diff] [blame] | 441 | unsigned char rates[NL80211_MAX_SUPP_RATES]; |
| 442 | int bintval, dtim_period, i, n_rates = 0; |
| 443 | char *end, *value = NULL, *sptr = NULL; |
Chun-Yeow Yeoh | 957baf8 | 2014-09-23 09:14:48 +0800 | [diff] [blame] | 444 | unsigned long freq = 0; |
| 445 | static const struct { |
| 446 | const char *name; |
| 447 | unsigned int width; |
| 448 | int freq1_diff; |
| 449 | int chantype; /* for older kernel */ |
| 450 | } *chanmode_selected = NULL, chanmode[] = { |
| 451 | { .name = "HT20", |
| 452 | .width = NL80211_CHAN_WIDTH_20, |
| 453 | .freq1_diff = 0, |
| 454 | .chantype = NL80211_CHAN_HT20 }, |
| 455 | { .name = "HT40+", |
| 456 | .width = NL80211_CHAN_WIDTH_40, |
| 457 | .freq1_diff = 10, |
| 458 | .chantype = NL80211_CHAN_HT40PLUS }, |
| 459 | { .name = "HT40-", |
| 460 | .width = NL80211_CHAN_WIDTH_40, |
| 461 | .freq1_diff = -10, |
| 462 | .chantype = NL80211_CHAN_HT40MINUS }, |
| 463 | { .name = "NOHT", |
| 464 | .width = NL80211_CHAN_WIDTH_20_NOHT, |
| 465 | .freq1_diff = 0, |
| 466 | .chantype = NL80211_CHAN_NO_HT }, |
| 467 | }; |
Chun-Yeow Yeoh | 7d01a09 | 2011-12-08 10:25:15 -0800 | [diff] [blame] | 468 | |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 469 | if (argc < 1) |
| 470 | return 1; |
| 471 | |
| 472 | NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(argv[0]), argv[0]); |
| 473 | argc--; |
| 474 | argv++; |
| 475 | |
Chun-Yeow Yeoh | 957baf8 | 2014-09-23 09:14:48 +0800 | [diff] [blame] | 476 | /* freq */ |
| 477 | if (argc > 1 && strcmp(argv[0], "freq") == 0) { |
| 478 | argv++; |
| 479 | argc--; |
| 480 | |
| 481 | freq = strtoul(argv[0], &end, 10); |
| 482 | if (*end != '\0') |
| 483 | return 1; |
| 484 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); |
| 485 | |
| 486 | argv++; |
| 487 | argc--; |
| 488 | } |
| 489 | |
| 490 | /* channel type */ |
| 491 | if (argc) { |
| 492 | for (i = 0; i < ARRAY_SIZE(chanmode); i++) { |
| 493 | if (strcasecmp(chanmode[i].name, argv[0]) == 0) { |
| 494 | chanmode_selected = &chanmode[i]; |
| 495 | break; |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | if (chanmode_selected) { |
| 500 | NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, |
| 501 | chanmode_selected->width); |
| 502 | NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, |
| 503 | freq + chanmode_selected->freq1_diff); |
| 504 | if (chanmode_selected->chantype != -1) |
| 505 | NLA_PUT_U32(msg, |
| 506 | NL80211_ATTR_WIPHY_CHANNEL_TYPE, |
| 507 | chanmode_selected->chantype); |
| 508 | |
| 509 | argv++; |
| 510 | argc--; |
| 511 | } |
| 512 | } |
| 513 | |
Chun-Yeow Yeoh | c8473cb | 2014-09-23 09:14:49 +0800 | [diff] [blame] | 514 | /* basic rates */ |
| 515 | if (argc > 1 && strcmp(argv[0], "basic-rates") == 0) { |
| 516 | argv++; |
| 517 | argc--; |
| 518 | |
| 519 | value = strtok_r(argv[0], ",", &sptr); |
| 520 | |
| 521 | while (value && n_rates < NL80211_MAX_SUPP_RATES) { |
| 522 | rate = strtod(value, &end); |
| 523 | rates[n_rates] = rate * 2; |
| 524 | |
| 525 | /* filter out suspicious values */ |
| 526 | if (*end != '\0' || !rates[n_rates] || |
| 527 | rate*2 != rates[n_rates]) |
| 528 | return 1; |
| 529 | |
| 530 | n_rates++; |
| 531 | value = strtok_r(NULL, ",", &sptr); |
| 532 | } |
| 533 | |
| 534 | NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, n_rates, rates); |
| 535 | argv++; |
| 536 | argc--; |
| 537 | } |
| 538 | |
| 539 | /* multicast rate */ |
Chun-Yeow Yeoh | 7d01a09 | 2011-12-08 10:25:15 -0800 | [diff] [blame] | 540 | if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) { |
| 541 | argv++; |
| 542 | argc--; |
| 543 | |
| 544 | rate = strtod(argv[0], &end); |
| 545 | if (*end != '\0') |
| 546 | return 1; |
| 547 | |
| 548 | NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10)); |
| 549 | argv++; |
| 550 | argc--; |
| 551 | } |
| 552 | |
Marco Porsch | a8c2e76 | 2013-01-18 13:05:30 +0100 | [diff] [blame] | 553 | if (argc > 1 && strcmp(argv[0], "beacon-interval") == 0) { |
| 554 | argc--; |
| 555 | argv++; |
| 556 | |
| 557 | bintval = strtoul(argv[0], &end, 10); |
| 558 | if (*end != '\0') |
| 559 | return 1; |
| 560 | |
| 561 | NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, bintval); |
| 562 | argv++; |
| 563 | argc--; |
| 564 | } |
| 565 | |
| 566 | if (argc > 1 && strcmp(argv[0], "dtim-period") == 0) { |
| 567 | argc--; |
| 568 | argv++; |
| 569 | |
| 570 | dtim_period = strtoul(argv[0], &end, 10); |
| 571 | if (*end != '\0') |
| 572 | return 1; |
| 573 | |
| 574 | NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); |
| 575 | argv++; |
| 576 | argc--; |
| 577 | } |
| 578 | |
Ashok Nagarajan | 9560e43 | 2012-04-12 17:45:13 -0700 | [diff] [blame] | 579 | container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP); |
| 580 | if (!container) |
| 581 | return -ENOBUFS; |
| 582 | |
| 583 | if (argc > 1 && strcmp(argv[0], "vendor_sync") == 0) { |
| 584 | argv++; |
| 585 | argc--; |
| 586 | if (strcmp(argv[0], "on") == 0) |
| 587 | NLA_PUT_U8(msg, |
| 588 | NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 1); |
| 589 | else |
| 590 | NLA_PUT_U8(msg, |
| 591 | NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 0); |
| 592 | argv++; |
| 593 | argc--; |
| 594 | } |
| 595 | /* parse and put other NL80211_ATTR_MESH_SETUP elements here */ |
| 596 | |
| 597 | nla_nest_end(msg, container); |
| 598 | |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 599 | if (!argc) |
| 600 | return 0; |
Johannes Berg | 05514f9 | 2012-07-19 11:50:50 +0200 | [diff] [blame] | 601 | return set_interface_meshparam(state, cb, msg, argc, argv, id); |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 602 | nla_put_failure: |
| 603 | return -ENOBUFS; |
| 604 | } |
Chun-Yeow Yeoh | c8473cb | 2014-09-23 09:14:49 +0800 | [diff] [blame] | 605 | COMMAND(mesh, join, "<mesh ID> [[freq <freq in MHz> <HT20|HT40+|HT40-|NOHT>]" |
| 606 | " [basic-rates <rate in Mbps,rate2,...>]], [mcast-rate <rate in Mbps>]" |
Marco Porsch | a8c2e76 | 2013-01-18 13:05:30 +0100 | [diff] [blame] | 607 | " [beacon-interval <time in TUs>] [dtim-period <value>]" |
| 608 | " [vendor_sync on|off] [<param>=<value>]*", |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 609 | NL80211_CMD_JOIN_MESH, 0, CIB_NETDEV, join_mesh, |
Chun-Yeow Yeoh | c8473cb | 2014-09-23 09:14:49 +0800 | [diff] [blame] | 610 | "Join a mesh with the given mesh ID with frequency, basic-rates,\n" |
| 611 | "mcast-rate and mesh parameters. Basic-rates are applied only if\n" |
| 612 | "frequency is provided."); |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 613 | |
| 614 | static int leave_mesh(struct nl80211_state *state, struct nl_cb *cb, |
Johannes Berg | 05514f9 | 2012-07-19 11:50:50 +0200 | [diff] [blame] | 615 | struct nl_msg *msg, int argc, char **argv, |
| 616 | enum id_input id) |
Johannes Berg | 400efd3 | 2010-12-02 08:58:57 +0100 | [diff] [blame] | 617 | { |
| 618 | if (argc) |
| 619 | return 1; |
| 620 | |
| 621 | return 0; |
| 622 | } |
| 623 | COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh, |
| 624 | "Leave a mesh."); |