1/* 2 * uci.c: UCI binding for the switch configuration utility 3 * 4 * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * version 2 as published by the Free Software Foundatio. 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 16#include <stdio.h> 17#include <string.h> 18#include <stdlib.h> 19#include <inttypes.h> 20#include <errno.h> 21#include <stdint.h> 22#include <getopt.h> 23#include <sys/types.h> 24#include <sys/socket.h> 25#include <uci.h> 26 27#include <linux/types.h> 28#include <linux/netlink.h> 29#include <linux/genetlink.h> 30#include <netlink/netlink.h> 31#include <netlink/genl/genl.h> 32#include <netlink/genl/ctrl.h> 33#include <linux/switch.h> 34#include "swlib.h" 35 36#ifndef ARRAY_SIZE 37#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 38#endif 39 40struct swlib_setting { 41 struct switch_attr *attr; 42 const char *name; 43 int port_vlan; 44 const char *val; 45 struct swlib_setting *next; 46}; 47 48struct swlib_setting early_settings[] = { 49 { .name = "reset", .val = "1" }, 50 { .name = "enable_vlan", .val = "1" }, 51}; 52 53static struct swlib_setting *settings; 54static struct swlib_setting **head; 55 56static bool swlib_match_name(struct switch_dev *dev, const char *name) 57{ 58 return (strcmp(name, dev->dev_name) == 0 || 59 strcmp(name, dev->alias) == 0); 60} 61 62static int 63swlib_map_settings(struct switch_dev *dev, int type, int port_vlan, struct uci_section *s) 64{ 65 struct swlib_setting *setting; 66 struct switch_attr *attr; 67 struct uci_element *e; 68 struct uci_option *o; 69 70 uci_foreach_element(&s->options, e) { 71 o = uci_to_option(e); 72 73 if (o->type != UCI_TYPE_STRING) 74 continue; 75 76 if (!strcmp(e->name, "device")) 77 continue; 78 79 /* map early settings */ 80 if (type == SWLIB_ATTR_GROUP_GLOBAL) { 81 int i; 82 83 for (i = 0; i < ARRAY_SIZE(early_settings); i++) { 84 if (strcmp(e->name, early_settings[i].name) != 0) 85 continue; 86 87 early_settings[i].val = o->v.string; 88 goto skip; 89 } 90 } 91 92 attr = swlib_lookup_attr(dev, type, e->name); 93 if (!attr) 94 continue; 95 96 setting = malloc(sizeof(struct swlib_setting)); 97 memset(setting, 0, sizeof(struct swlib_setting)); 98 setting->attr = attr; 99 setting->port_vlan = port_vlan; 100 setting->val = o->v.string; 101 *head = setting; 102 head = &setting->next; 103skip: 104 continue; 105 } 106} 107 108int swlib_apply_from_uci(struct switch_dev *dev, struct uci_package *p) 109{ 110 struct switch_attr *attr; 111 struct uci_element *e; 112 struct uci_section *s; 113 struct uci_option *o; 114 struct uci_ptr ptr; 115 struct switch_val val; 116 int i; 117 118 settings = NULL; 119 head = &settings; 120 121 uci_foreach_element(&p->sections, e) { 122 struct uci_element *n; 123 124 s = uci_to_section(e); 125 126 if (strcmp(s->type, "switch") != 0) 127 continue; 128 129 uci_foreach_element(&s->options, n) { 130 struct uci_option *o = uci_to_option(n); 131 132 if (strcmp(n->name, "name") != 0) 133 continue; 134 135 if (o->type != UCI_TYPE_STRING) 136 continue; 137 138 if (swlib_match_name(dev, o->v.string)) 139 goto found; 140 141 break; 142 } 143 144 if (!swlib_match_name(dev, e->name)) 145 continue; 146 147 goto found; 148 } 149 150 /* not found */ 151 return -1; 152 153found: 154 /* look up available early options, which need to be taken care 155 * of in the correct order */ 156 for (i = 0; i < ARRAY_SIZE(early_settings); i++) { 157 early_settings[i].attr = swlib_lookup_attr(dev, 158 SWLIB_ATTR_GROUP_GLOBAL, early_settings[i].name); 159 } 160 swlib_map_settings(dev, SWLIB_ATTR_GROUP_GLOBAL, 0, s); 161 162 /* look for port or vlan sections */ 163 uci_foreach_element(&p->sections, e) { 164 struct uci_element *os; 165 s = uci_to_section(e); 166 167 if (!strcmp(s->type, "switch_port")) { 168 char *devn, *port, *port_err = NULL; 169 int port_n; 170 171 uci_foreach_element(&s->options, os) { 172 o = uci_to_option(os); 173 if (o->type != UCI_TYPE_STRING) 174 continue; 175 176 if (!strcmp(os->name, "device")) { 177 devn = o->v.string; 178 if (!swlib_match_name(dev, devn)) 179 devn = NULL; 180 } else if (!strcmp(os->name, "port")) { 181 port = o->v.string; 182 } 183 } 184 if (!devn || !port || !port[0]) 185 continue; 186 187 port_n = strtoul(port, &port_err, 0); 188 if (port_err && port_err[0]) 189 continue; 190 191 swlib_map_settings(dev, SWLIB_ATTR_GROUP_PORT, port_n, s); 192 } else if (!strcmp(s->type, "switch_vlan")) { 193 char *devn, *vlan, *vlan_err = NULL; 194 int vlan_n; 195 196 uci_foreach_element(&s->options, os) { 197 o = uci_to_option(os); 198 if (o->type != UCI_TYPE_STRING) 199 continue; 200 201 if (!strcmp(os->name, "device")) { 202 devn = o->v.string; 203 if (!swlib_match_name(dev, devn)) 204 devn = NULL; 205 } else if (!strcmp(os->name, "vlan")) { 206 vlan = o->v.string; 207 } 208 } 209 if (!devn || !vlan || !vlan[0]) 210 continue; 211 212 vlan_n = strtoul(vlan, &vlan_err, 0); 213 if (vlan_err && vlan_err[0]) 214 continue; 215 216 swlib_map_settings(dev, SWLIB_ATTR_GROUP_VLAN, vlan_n, s); 217 } 218 } 219 220 for (i = 0; i < ARRAY_SIZE(early_settings); i++) { 221 struct swlib_setting *st = &early_settings[i]; 222 if (!st->attr || !st->val) 223 continue; 224 swlib_set_attr_string(dev, st->attr, st->port_vlan, st->val); 225 226 } 227 228 while (settings) { 229 struct swlib_setting *st = settings; 230 231 swlib_set_attr_string(dev, st->attr, st->port_vlan, st->val); 232 st = st->next; 233 free(settings); 234 settings = st; 235 } 236 237 /* Apply the config */ 238 attr = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, "apply"); 239 if (!attr) 240 return 0; 241 242 memset(&val, 0, sizeof(val)); 243 swlib_set_attr(dev, attr, &val); 244 245 return 0; 246} 247