1/* 2 * swconfig.c: Switch configuration utility 3 * 4 * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name> 5 * Copyright (C) 2010 Martin Mares <mj@ucw.cz> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundatio. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17#include <stdio.h> 18#include <string.h> 19#include <stdlib.h> 20#include <inttypes.h> 21#include <errno.h> 22#include <stdint.h> 23#include <getopt.h> 24#include <sys/types.h> 25#include <sys/socket.h> 26#include <uci.h> 27 28#include <linux/types.h> 29#include <linux/netlink.h> 30#include <linux/genetlink.h> 31#include <netlink/netlink.h> 32#include <netlink/genl/genl.h> 33#include <netlink/genl/ctrl.h> 34#include <linux/switch.h> 35#include "swlib.h" 36 37enum { 38 CMD_NONE, 39 CMD_GET, 40 CMD_SET, 41 CMD_LOAD, 42 CMD_HELP, 43 CMD_SHOW, 44 CMD_PORTMAP, 45}; 46 47static void 48print_attrs(const struct switch_attr *attr) 49{ 50 int i = 0; 51 while (attr) { 52 const char *type; 53 switch(attr->type) { 54 case SWITCH_TYPE_INT: 55 type = "int"; 56 break; 57 case SWITCH_TYPE_STRING: 58 type = "string"; 59 break; 60 case SWITCH_TYPE_PORTS: 61 type = "ports"; 62 break; 63 case SWITCH_TYPE_NOVAL: 64 type = "none"; 65 break; 66 default: 67 type = "unknown"; 68 break; 69 } 70 printf("\tAttribute %d (%s): %s (%s)\n", ++i, type, attr->name, attr->description); 71 attr = attr->next; 72 } 73} 74 75static void 76list_attributes(struct switch_dev *dev) 77{ 78 printf("%s: %s(%s), ports: %d (cpu @ %d), vlans: %d\n", dev->dev_name, dev->alias, dev->name, dev->ports, dev->cpu_port, dev->vlans); 79 printf(" --switch\n"); 80 print_attrs(dev->ops); 81 printf(" --vlan\n"); 82 print_attrs(dev->vlan_ops); 83 printf(" --port\n"); 84 print_attrs(dev->port_ops); 85} 86 87static const char * 88speed_str(int speed) 89{ 90 switch (speed) { 91 case 10: 92 return "10baseT"; 93 case 100: 94 return "100baseT"; 95 case 1000: 96 return "1000baseT"; 97 default: 98 break; 99 } 100 101 return "unknown"; 102} 103 104static void 105print_attr_val(const struct switch_attr *attr, const struct switch_val *val) 106{ 107 struct switch_port_link *link; 108 int i; 109 110 switch (attr->type) { 111 case SWITCH_TYPE_INT: 112 printf("%d", val->value.i); 113 break; 114 case SWITCH_TYPE_STRING: 115 printf("%s", val->value.s); 116 break; 117 case SWITCH_TYPE_PORTS: 118 for(i = 0; i < val->len; i++) { 119 printf("%d%s ", 120 val->value.ports[i].id, 121 (val->value.ports[i].flags & 122 SWLIB_PORT_FLAG_TAGGED) ? "t" : ""); 123 } 124 break; 125 case SWITCH_TYPE_LINK: 126 link = val->value.link; 127 if (link->link) 128 printf("port:%d link:up speed:%s %s-duplex %s%s%s%s%s", 129 val->port_vlan, 130 speed_str(link->speed), 131 link->duplex ? "full" : "half", 132 link->tx_flow ? "txflow " : "", 133 link->rx_flow ? "rxflow " : "", 134 link->eee & SWLIB_LINK_FLAG_EEE_100BASET ? "eee100 " : "", 135 link->eee & SWLIB_LINK_FLAG_EEE_1000BASET ? "eee1000 " : "", 136 link->aneg ? "auto" : ""); 137 else 138 printf("port:%d link:down", val->port_vlan); 139 break; 140 default: 141 printf("?unknown-type?"); 142 } 143} 144 145static void 146show_attrs(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) 147{ 148 while (attr) { 149 if (attr->type != SWITCH_TYPE_NOVAL) { 150 printf("\t%s: ", attr->name); 151 if (swlib_get_attr(dev, attr, val) < 0) 152 printf("???"); 153 else 154 print_attr_val(attr, val); 155 putchar('\n'); 156 } 157 attr = attr->next; 158 } 159} 160 161static void 162show_global(struct switch_dev *dev) 163{ 164 struct switch_val val; 165 166 printf("Global attributes:\n"); 167 show_attrs(dev, dev->ops, &val); 168} 169 170static void 171show_port(struct switch_dev *dev, int port) 172{ 173 struct switch_val val; 174 175 printf("Port %d:\n", port); 176 val.port_vlan = port; 177 show_attrs(dev, dev->port_ops, &val); 178} 179 180static void 181show_vlan(struct switch_dev *dev, int vlan, bool all) 182{ 183 struct switch_val val; 184 struct switch_attr *attr; 185 186 val.port_vlan = vlan; 187 188 if (all) { 189 attr = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, "ports"); 190 if (swlib_get_attr(dev, attr, &val) < 0) 191 return; 192 193 if (!val.len) 194 return; 195 } 196 197 printf("VLAN %d:\n", vlan); 198 show_attrs(dev, dev->vlan_ops, &val); 199} 200 201static void 202print_usage(void) 203{ 204 printf("swconfig list\n"); 205 printf("swconfig dev <dev> [port <port>|vlan <vlan>] (help|set <key> <value>|get <key>|load <config>|show)\n"); 206 exit(1); 207} 208 209static void 210swconfig_load_uci(struct switch_dev *dev, const char *name) 211{ 212 struct uci_context *ctx; 213 struct uci_package *p = NULL; 214 int ret = -1; 215 216 ctx = uci_alloc_context(); 217 if (!ctx) 218 return; 219 220 uci_load(ctx, name, &p); 221 if (!p) { 222 uci_perror(ctx, "Failed to load config file: "); 223 goto out; 224 } 225 226 ret = swlib_apply_from_uci(dev, p); 227 if (ret < 0) 228 fprintf(stderr, "Failed to apply configuration for switch '%s'\n", dev->dev_name); 229 230out: 231 uci_free_context(ctx); 232 exit(ret); 233} 234 235int main(int argc, char **argv) 236{ 237 int retval = 0; 238 struct switch_dev *dev; 239 struct switch_attr *a; 240 struct switch_val val; 241 int i; 242 243 int cmd = CMD_NONE; 244 char *cdev = NULL; 245 int cport = -1; 246 int cvlan = -1; 247 char *ckey = NULL; 248 char *cvalue = NULL; 249 char *csegment = NULL; 250 251 if((argc == 2) && !strcmp(argv[1], "list")) { 252 swlib_list(); 253 return 0; 254 } 255 256 if(argc < 4) 257 print_usage(); 258 259 if(strcmp(argv[1], "dev")) 260 print_usage(); 261 262 cdev = argv[2]; 263 264 for(i = 3; i < argc; i++) 265 { 266 char *arg = argv[i]; 267 if (cmd != CMD_NONE) { 268 print_usage(); 269 } else if (!strcmp(arg, "port") && i+1 < argc) { 270 cport = atoi(argv[++i]); 271 } else if (!strcmp(arg, "vlan") && i+1 < argc) { 272 cvlan = atoi(argv[++i]); 273 } else if (!strcmp(arg, "help")) { 274 cmd = CMD_HELP; 275 } else if (!strcmp(arg, "set") && i+1 < argc) { 276 cmd = CMD_SET; 277 ckey = argv[++i]; 278 if (i+1 < argc) 279 cvalue = argv[++i]; 280 } else if (!strcmp(arg, "get") && i+1 < argc) { 281 cmd = CMD_GET; 282 ckey = argv[++i]; 283 } else if (!strcmp(arg, "load") && i+1 < argc) { 284 if ((cport >= 0) || (cvlan >= 0)) 285 print_usage(); 286 cmd = CMD_LOAD; 287 ckey = argv[++i]; 288 } else if (!strcmp(arg, "portmap")) { 289 if (i + 1 < argc) 290 csegment = argv[++i]; 291 cmd = CMD_PORTMAP; 292 } else if (!strcmp(arg, "show")) { 293 cmd = CMD_SHOW; 294 } else { 295 print_usage(); 296 } 297 } 298 299 if (cmd == CMD_NONE) 300 print_usage(); 301 if (cport > -1 && cvlan > -1) 302 print_usage(); 303 304 dev = swlib_connect(cdev); 305 if (!dev) { 306 fprintf(stderr, "Failed to connect to the switch. Use the \"list\" command to see which switches are available.\n"); 307 return 1; 308 } 309 310 swlib_scan(dev); 311 312 if (cmd == CMD_GET || cmd == CMD_SET) { 313 if(cport > -1) 314 a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_PORT, ckey); 315 else if(cvlan > -1) 316 a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, ckey); 317 else 318 a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, ckey); 319 320 if(!a) 321 { 322 fprintf(stderr, "Unknown attribute \"%s\"\n", ckey); 323 retval = -1; 324 goto out; 325 } 326 } 327 328 switch(cmd) 329 { 330 case CMD_SET: 331 if ((a->type != SWITCH_TYPE_NOVAL) && 332 (cvalue == NULL)) 333 print_usage(); 334 335 if(cvlan > -1) 336 cport = cvlan; 337 338 retval = swlib_set_attr_string(dev, a, cport, cvalue); 339 if (retval < 0) 340 { 341 nl_perror(-retval, "Failed to set attribute"); 342 goto out; 343 } 344 break; 345 case CMD_GET: 346 if(cvlan > -1) 347 val.port_vlan = cvlan; 348 if(cport > -1) 349 val.port_vlan = cport; 350 retval = swlib_get_attr(dev, a, &val); 351 if (retval < 0) 352 { 353 nl_perror(-retval, "Failed to get attribute"); 354 goto out; 355 } 356 print_attr_val(a, &val); 357 putchar('\n'); 358 break; 359 case CMD_LOAD: 360 swconfig_load_uci(dev, ckey); 361 break; 362 case CMD_HELP: 363 list_attributes(dev); 364 break; 365 case CMD_PORTMAP: 366 swlib_print_portmap(dev, csegment); 367 break; 368 case CMD_SHOW: 369 if (cport >= 0 || cvlan >= 0) { 370 if (cport >= 0) 371 show_port(dev, cport); 372 else 373 show_vlan(dev, cvlan, false); 374 } else { 375 show_global(dev); 376 for (i=0; i < dev->ports; i++) 377 show_port(dev, i); 378 for (i=0; i < dev->vlans; i++) 379 show_vlan(dev, i, true); 380 } 381 break; 382 } 383 384out: 385 swlib_free_all(dev); 386 return retval; 387} 388