| @@ -35,7 +35,8 @@ · 34 unmodified lines |
| 35 | 35 | #include <net/xfrm.h> |
| 36 | 36 | #include <linux/netpoll.h> |
| 37 | 37 | |
| 38 | | −#define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) |
| 38 | +#define MACVLAN_HASH_BITS 8 |
| 39 | +#define MACVLAN_HASH_SIZE (1<<MACVLAN_HASH_BITS) |
| 39 | 40 | #define MACVLAN_BC_QUEUE_LEN 1000 |
| 40 | 41 | |
| 41 | 42 | struct macvlan_port { |
| @@ -47,6 +48,14 @@ struct macvlan_port { · 5 unmodified lines |
| 47 | 48 | struct work_struct bc_work; |
| 48 | 49 | bool passthru; |
| 49 | 50 | int count; |
| 51 | + struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE]; |
| 52 | +}; |
| 53 | + |
| 54 | +struct macvlan_source_entry { |
| 55 | + struct hlist_node hlist; |
| 56 | + struct macvlan_dev *vlan; |
| 57 | + unsigned char addr[6+2] __aligned(sizeof(u16)); |
| 58 | + struct rcu_head rcu; |
| 50 | 59 | }; |
| 51 | 60 | |
| 52 | 61 | struct macvlan_skb_cb { |
| @@ -57,6 +66,20 @@ struct macvlan_skb_cb { · 4 unmodified lines |
| 57 | 66 | |
| 58 | 67 | static void macvlan_port_destroy(struct net_device *dev); |
| 59 | 68 | |
| 69 | +/* Hash Ethernet address */ |
| 70 | +static u32 macvlan_eth_hash(const unsigned char *addr) |
| 71 | +{ |
| 72 | + u64 value = get_unaligned((u64 *)addr); |
| 73 | + |
| 74 | + /* only want 6 bytes */ |
| 75 | +#ifdef __BIG_ENDIAN |
| 76 | + value >>= 16; |
| 77 | +#else |
| 78 | + value <<= 16; |
| 79 | +#endif |
| 80 | + return hash_64(value, MACVLAN_HASH_BITS); |
| 81 | +} |
| 82 | + |
| 60 | 83 | static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev) |
| 61 | 84 | { |
| 62 | 85 | return rcu_dereference(dev->rx_handler_data); |
| @@ -73,20 +96,68 @@ static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, · 10 unmodified lines |
| 73 | 96 | const unsigned char *addr) |
| 74 | 97 | { |
| 75 | 98 | struct macvlan_dev *vlan; |
| 99 | + u32 idx = macvlan_eth_hash(addr); |
| 76 | 100 | |
| 77 | | − hlist_for_each_entry_rcu(vlan, &port->vlan_hash[addr[5]], hlist) { |
| 101 | + hlist_for_each_entry_rcu(vlan, &port->vlan_hash[idx], hlist) { |
| 78 | 102 | if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr)) |
| 79 | 103 | return vlan; |
| 80 | 104 | } |
| 81 | 105 | return NULL; |
| 82 | 106 | } |
| 83 | 107 | |
| 108 | +static struct macvlan_source_entry *macvlan_hash_lookup_source( |
| 109 | + const struct macvlan_dev *vlan, |
| 110 | + const unsigned char *addr) |
| 111 | +{ |
| 112 | + struct macvlan_source_entry *entry; |
| 113 | + u32 idx = macvlan_eth_hash(addr); |
| 114 | + struct hlist_head *h = &vlan->port->vlan_source_hash[idx]; |
| 115 | + |
| 116 | + hlist_for_each_entry_rcu(entry, h, hlist) { |
| 117 | + if (ether_addr_equal_64bits(entry->addr, addr) && |
| 118 | + entry->vlan == vlan) |
| 119 | + return entry; |
| 120 | + } |
| 121 | + return NULL; |
| 122 | +} |
| 123 | + |
| 124 | +static int macvlan_hash_add_source(struct macvlan_dev *vlan, |
| 125 | + const unsigned char *addr) |
| 126 | +{ |
| 127 | + struct macvlan_port *port = vlan->port; |
| 128 | + struct macvlan_source_entry *entry; |
| 129 | + struct hlist_head *h; |
| 130 | + |
| 131 | + entry = macvlan_hash_lookup_source(vlan, addr); |
| 132 | + if (entry) |
| 133 | + return 0; |
| 134 | + |
| 135 | + entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
| 136 | + if (!entry) |
| 137 | + return -ENOMEM; |
| 138 | + |
| 139 | + ether_addr_copy(entry->addr, addr); |
| 140 | + entry->vlan = vlan; |
| 141 | + h = &port->vlan_source_hash[macvlan_eth_hash(addr)]; |
| 142 | + hlist_add_head_rcu(&entry->hlist, h); |
| 143 | + vlan->macaddr_count++; |
| 144 | + |
| 145 | + return 0; |
| 146 | +} |
| 147 | + |
| 84 | 148 | static void macvlan_hash_add(struct macvlan_dev *vlan) |
| 85 | 149 | { |
| 86 | 150 | struct macvlan_port *port = vlan->port; |
| 87 | 151 | const unsigned char *addr = vlan->dev->dev_addr; |
| 152 | + u32 idx = macvlan_eth_hash(addr); |
| 88 | 153 | |
| 89 | | − hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[addr[5]]); |
| 154 | + hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[idx]); |
| 155 | +} |
| 156 | + |
| 157 | +static void macvlan_hash_del_source(struct macvlan_source_entry *entry) |
| 158 | +{ |
| 159 | + hlist_del_rcu(&entry->hlist); |
| 160 | + kfree_rcu(entry, rcu); |
| 90 | 161 | } |
| 91 | 162 | |
| 92 | 163 | static void macvlan_hash_del(struct macvlan_dev *vlan, bool sync) |
| @@ -267,6 +338,65 @@ err: · 174 unmodified lines |
| 267 | 338 | atomic_long_inc(&skb->dev->rx_dropped); |
| 268 | 339 | } |
| 269 | 340 | |
| 341 | +static void macvlan_flush_sources(struct macvlan_port *port, |
| 342 | + struct macvlan_dev *vlan) |
| 343 | +{ |
| 344 | + int i; |
| 345 | + |
| 346 | + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { |
| 347 | + struct hlist_node *h, *n; |
| 348 | + |
| 349 | + hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) { |
| 350 | + struct macvlan_source_entry *entry; |
| 351 | + |
| 352 | + entry = hlist_entry(h, struct macvlan_source_entry, |
| 353 | + hlist); |
| 354 | + if (entry->vlan == vlan) |
| 355 | + macvlan_hash_del_source(entry); |
| 356 | + } |
| 357 | + } |
| 358 | + vlan->macaddr_count = 0; |
| 359 | +} |
| 360 | + |
| 361 | +static void macvlan_forward_source_one(struct sk_buff *skb, |
| 362 | + struct macvlan_dev *vlan) |
| 363 | +{ |
| 364 | + struct sk_buff *nskb; |
| 365 | + struct net_device *dev; |
| 366 | + int len; |
| 367 | + int ret; |
| 368 | + |
| 369 | + dev = vlan->dev; |
| 370 | + if (unlikely(!(dev->flags & IFF_UP))) |
| 371 | + return; |
| 372 | + |
| 373 | + nskb = skb_clone(skb, GFP_ATOMIC); |
| 374 | + if (!nskb) |
| 375 | + return; |
| 376 | + |
| 377 | + len = nskb->len + ETH_HLEN; |
| 378 | + nskb->dev = dev; |
| 379 | + nskb->pkt_type = PACKET_HOST; |
| 380 | + |
| 381 | + ret = netif_rx(nskb); |
| 382 | + macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, 0); |
| 383 | +} |
| 384 | + |
| 385 | +static void macvlan_forward_source(struct sk_buff *skb, |
| 386 | + struct macvlan_port *port, |
| 387 | + const unsigned char *addr) |
| 388 | +{ |
| 389 | + struct macvlan_source_entry *entry; |
| 390 | + u32 idx = macvlan_eth_hash(addr); |
| 391 | + struct hlist_head *h = &port->vlan_source_hash[idx]; |
| 392 | + |
| 393 | + hlist_for_each_entry_rcu(entry, h, hlist) { |
| 394 | + if (ether_addr_equal_64bits(entry->addr, addr)) |
| 395 | + if (entry->vlan->dev->flags & IFF_UP) |
| 396 | + macvlan_forward_source_one(skb, entry->vlan); |
| 397 | + } |
| 398 | +} |
| 399 | + |
| 270 | 400 | /* called under rcu_read_lock() from netif_receive_skb */ |
| 271 | 401 | static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) |
| 272 | 402 | { |
| @@ -285,6 +415,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) · 12 unmodified lines |
| 285 | 415 | if (!skb) |
| 286 | 416 | return RX_HANDLER_CONSUMED; |
| 287 | 417 | eth = eth_hdr(skb); |
| 418 | + macvlan_forward_source(skb, port, eth->h_source); |
| 288 | 419 | src = macvlan_hash_lookup(port, eth->h_source); |
| 289 | 420 | if (src && src->mode != MACVLAN_MODE_VEPA && |
| 290 | 421 | src->mode != MACVLAN_MODE_BRIDGE) { |
| @@ -301,6 +432,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) · 10 unmodified lines |
| 301 | 432 | return RX_HANDLER_PASS; |
| 302 | 433 | } |
| 303 | 434 | |
| 435 | + macvlan_forward_source(skb, port, eth->h_source); |
| 304 | 436 | if (port->passthru) |
| 305 | 437 | vlan = list_first_or_null_rcu(&port->vlans, |
| 306 | 438 | struct macvlan_dev, list); |
| @@ -667,6 +799,7 @@ static void macvlan_uninit(struct net_device *dev) · 360 unmodified lines |
| 667 | 799 | |
| 668 | 800 | free_percpu(vlan->pcpu_stats); |
| 669 | 801 | |
| 802 | + macvlan_flush_sources(port, vlan); |
| 670 | 803 | port->count -= 1; |
| 671 | 804 | if (!port->count) |
| 672 | 805 | macvlan_port_destroy(port->dev); |
| @@ -925,6 +1058,8 @@ static int macvlan_port_create(struct net_device *dev) · 252 unmodified lines |
| 925 | 1058 | INIT_LIST_HEAD(&port->vlans); |
| 926 | 1059 | for (i = 0; i < MACVLAN_HASH_SIZE; i++) |
| 927 | 1060 | INIT_HLIST_HEAD(&port->vlan_hash[i]); |
| 1061 | + for (i = 0; i < MACVLAN_HASH_SIZE; i++) |
| 1062 | + INIT_HLIST_HEAD(&port->vlan_source_hash[i]); |
| 928 | 1063 | |
| 929 | 1064 | skb_queue_head_init(&port->bc_queue); |
| 930 | 1065 | INIT_WORK(&port->bc_work, macvlan_process_broadcast); |
| @@ -966,11 +1101,102 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) · 35 unmodified lines |
| 966 | 1101 | case MACVLAN_MODE_VEPA: |
| 967 | 1102 | case MACVLAN_MODE_BRIDGE: |
| 968 | 1103 | case MACVLAN_MODE_PASSTHRU: |
| 1104 | + case MACVLAN_MODE_SOURCE: |
| 1105 | + break; |
| 1106 | + default: |
| 1107 | + return -EINVAL; |
| 1108 | + } |
| 1109 | + } |
| 1110 | + |
| 1111 | + if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { |
| 1112 | + switch (nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE])) { |
| 1113 | + case MACVLAN_MACADDR_ADD: |
| 1114 | + case MACVLAN_MACADDR_DEL: |
| 1115 | + case MACVLAN_MACADDR_FLUSH: |
| 1116 | + case MACVLAN_MACADDR_SET: |
| 969 | 1117 | break; |
| 970 | 1118 | default: |
| 971 | 1119 | return -EINVAL; |
| 972 | 1120 | } |
| 973 | 1121 | } |
| 1122 | + |
| 1123 | + if (data && data[IFLA_MACVLAN_MACADDR]) { |
| 1124 | + if (nla_len(data[IFLA_MACVLAN_MACADDR]) != ETH_ALEN) |
| 1125 | + return -EINVAL; |
| 1126 | + |
| 1127 | + if (!is_valid_ether_addr(nla_data(data[IFLA_MACVLAN_MACADDR]))) |
| 1128 | + return -EADDRNOTAVAIL; |
| 1129 | + } |
| 1130 | + |
| 1131 | + if (data && data[IFLA_MACVLAN_MACADDR_COUNT]) |
| 1132 | + return -EINVAL; |
| 1133 | + |
| 1134 | + return 0; |
| 1135 | +} |
| 1136 | + |
| 1137 | +/** |
| 1138 | + * reconfigure list of remote source mac address |
| 1139 | + * (only for macvlan devices in source mode) |
| 1140 | + * Note regarding alignment: all netlink data is aligned to 4 Byte, which |
| 1141 | + * suffices for both ether_addr_copy and ether_addr_equal_64bits usage. |
| 1142 | + */ |
| 1143 | +static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode, |
| 1144 | + struct nlattr *data[]) |
| 1145 | +{ |
| 1146 | + char *addr = NULL; |
| 1147 | + int ret, rem, len; |
| 1148 | + struct nlattr *nla, *head; |
| 1149 | + struct macvlan_source_entry *entry; |
| 1150 | + |
| 1151 | + if (data[IFLA_MACVLAN_MACADDR]) |
| 1152 | + addr = nla_data(data[IFLA_MACVLAN_MACADDR]); |
| 1153 | + |
| 1154 | + if (mode == MACVLAN_MACADDR_ADD) { |
| 1155 | + if (!addr) |
| 1156 | + return -EINVAL; |
| 1157 | + |
| 1158 | + return macvlan_hash_add_source(vlan, addr); |
| 1159 | + |
| 1160 | + } else if (mode == MACVLAN_MACADDR_DEL) { |
| 1161 | + if (!addr) |
| 1162 | + return -EINVAL; |
| 1163 | + |
| 1164 | + entry = macvlan_hash_lookup_source(vlan, addr); |
| 1165 | + if (entry) { |
| 1166 | + macvlan_hash_del_source(entry); |
| 1167 | + vlan->macaddr_count--; |
| 1168 | + } |
| 1169 | + } else if (mode == MACVLAN_MACADDR_FLUSH) { |
| 1170 | + macvlan_flush_sources(vlan->port, vlan); |
| 1171 | + } else if (mode == MACVLAN_MACADDR_SET) { |
| 1172 | + macvlan_flush_sources(vlan->port, vlan); |
| 1173 | + |
| 1174 | + if (addr) { |
| 1175 | + ret = macvlan_hash_add_source(vlan, addr); |
| 1176 | + if (ret) |
| 1177 | + return ret; |
| 1178 | + } |
| 1179 | + |
| 1180 | + if (!data || !data[IFLA_MACVLAN_MACADDR_DATA]) |
| 1181 | + return 0; |
| 1182 | + |
| 1183 | + head = nla_data(data[IFLA_MACVLAN_MACADDR_DATA]); |
| 1184 | + len = nla_len(data[IFLA_MACVLAN_MACADDR_DATA]); |
| 1185 | + |
| 1186 | + nla_for_each_attr(nla, head, len, rem) { |
| 1187 | + if (nla_type(nla) != IFLA_MACVLAN_MACADDR || |
| 1188 | + nla_len(nla) != ETH_ALEN) |
| 1189 | + continue; |
| 1190 | + |
| 1191 | + addr = nla_data(nla); |
| 1192 | + ret = macvlan_hash_add_source(vlan, addr); |
| 1193 | + if (ret) |
| 1194 | + return ret; |
| 1195 | + } |
| 1196 | + } else { |
| 1197 | + return -EINVAL; |
| 1198 | + } |
| 1199 | + |
| 974 | 1200 | return 0; |
| 975 | 1201 | } |
| 976 | 1202 | |
| @@ -981,6 +1207,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, · 4 unmodified lines |
| 981 | 1207 | struct macvlan_port *port; |
| 982 | 1208 | struct net_device *lowerdev; |
| 983 | 1209 | int err; |
| 1210 | + int macmode; |
| 984 | 1211 | |
| 985 | 1212 | if (!tb[IFLA_LINK]) |
| 986 | 1213 | return -EINVAL; |
| @@ -1034,6 +1261,15 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, · 47 unmodified lines |
| 1034 | 1261 | eth_hw_addr_inherit(dev, lowerdev); |
| 1035 | 1262 | } |
| 1036 | 1263 | |
| 1264 | + if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { |
| 1265 | + if (vlan->mode != MACVLAN_MODE_SOURCE) |
| 1266 | + return -EINVAL; |
| 1267 | + macmode = nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]); |
| 1268 | + err = macvlan_changelink_sources(vlan, macmode, data); |
| 1269 | + if (err) |
| 1270 | + return err; |
| 1271 | + } |
| 1272 | + |
| 1037 | 1273 | port->count += 1; |
| 1038 | 1274 | err = register_netdevice(dev); |
| 1039 | 1275 | if (err < 0) |
| @@ -1070,6 +1306,8 @@ void macvlan_dellink(struct net_device *dev, struct list_head *head) · 30 unmodified lines |
| 1070 | 1306 | { |
| 1071 | 1307 | struct macvlan_dev *vlan = netdev_priv(dev); |
| 1072 | 1308 | |
| 1309 | + if (vlan->mode == MACVLAN_MODE_SOURCE) |
| 1310 | + macvlan_flush_sources(vlan->port, vlan); |
| 1073 | 1311 | list_del_rcu(&vlan->list); |
| 1074 | 1312 | unregister_netdevice_queue(dev, head); |
| 1075 | 1313 | netdev_upper_dev_unlink(vlan->lowerdev, dev); |
| @@ -1082,6 +1320,8 @@ static int macvlan_changelink(struct net_device *dev, · 6 unmodified lines |
| 1082 | 1320 | struct macvlan_dev *vlan = netdev_priv(dev); |
| 1083 | 1321 | enum macvlan_mode mode; |
| 1084 | 1322 | bool set_mode = false; |
| 1323 | + enum macvlan_macaddr_mode macmode; |
| 1324 | + int ret; |
| 1085 | 1325 | |
| 1086 | 1326 | /* Validate mode, but don't set yet: setting flags may fail. */ |
| 1087 | 1327 | if (data && data[IFLA_MACVLAN_MODE]) { |
| @@ -1091,6 +1331,9 @@ static int macvlan_changelink(struct net_device *dev, · 3 unmodified lines |
| 1091 | 1331 | if ((mode == MACVLAN_MODE_PASSTHRU) != |
| 1092 | 1332 | (vlan->mode == MACVLAN_MODE_PASSTHRU)) |
| 1093 | 1333 | return -EINVAL; |
| 1334 | + if (vlan->mode == MACVLAN_MODE_SOURCE && |
| 1335 | + vlan->mode != mode) |
| 1336 | + macvlan_flush_sources(vlan->port, vlan); |
| 1094 | 1337 | } |
| 1095 | 1338 | |
| 1096 | 1339 | if (data && data[IFLA_MACVLAN_FLAGS]) { |
| @@ -1110,26 +1353,77 @@ static int macvlan_changelink(struct net_device *dev, · 13 unmodified lines |
| 1110 | 1353 | } |
| 1111 | 1354 | if (set_mode) |
| 1112 | 1355 | vlan->mode = mode; |
| 1356 | + if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { |
| 1357 | + if (vlan->mode != MACVLAN_MODE_SOURCE) |
| 1358 | + return -EINVAL; |
| 1359 | + macmode = nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]); |
| 1360 | + ret = macvlan_changelink_sources(vlan, macmode, data); |
| 1361 | + if (ret) |
| 1362 | + return ret; |
| 1363 | + } |
| 1113 | 1364 | return 0; |
| 1114 | 1365 | } |
| 1115 | 1366 | |
| 1367 | +static size_t macvlan_get_size_mac(const struct macvlan_dev *vlan) |
| 1368 | +{ |
| 1369 | + if (vlan->macaddr_count == 0) |
| 1370 | + return 0; |
| 1371 | + return nla_total_size(0) /* IFLA_MACVLAN_MACADDR_DATA */ |
| 1372 | + + vlan->macaddr_count * nla_total_size(sizeof(u8) * ETH_ALEN); |
| 1373 | +} |
| 1374 | + |
| 1116 | 1375 | static size_t macvlan_get_size(const struct net_device *dev) |
| 1117 | 1376 | { |
| 1377 | + struct macvlan_dev *vlan = netdev_priv(dev); |
| 1378 | + |
| 1118 | 1379 | return (0 |
| 1119 | 1380 | + nla_total_size(4) /* IFLA_MACVLAN_MODE */ |
| 1120 | 1381 | + nla_total_size(2) /* IFLA_MACVLAN_FLAGS */ |
| 1382 | + + nla_total_size(4) /* IFLA_MACVLAN_MACADDR_COUNT */ |
| 1383 | + + macvlan_get_size_mac(vlan) /* IFLA_MACVLAN_MACADDR */ |
| 1121 | 1384 | ); |
| 1122 | 1385 | } |
| 1123 | 1386 | |
| 1387 | +static int macvlan_fill_info_macaddr(struct sk_buff *skb, |
| 1388 | + const struct macvlan_dev *vlan, |
| 1389 | + const int i) |
| 1390 | +{ |
| 1391 | + struct hlist_head *h = &vlan->port->vlan_source_hash[i]; |
| 1392 | + struct macvlan_source_entry *entry; |
| 1393 | + |
| 1394 | + hlist_for_each_entry_rcu(entry, h, hlist) { |
| 1395 | + if (entry->vlan != vlan) |
| 1396 | + continue; |
| 1397 | + if (nla_put(skb, IFLA_MACVLAN_MACADDR, ETH_ALEN, entry->addr)) |
| 1398 | + return 1; |
| 1399 | + } |
| 1400 | + return 0; |
| 1401 | +} |
| 1402 | + |
| 1124 | 1403 | static int macvlan_fill_info(struct sk_buff *skb, |
| 1125 | 1404 | const struct net_device *dev) |
| 1126 | 1405 | { |
| 1127 | 1406 | struct macvlan_dev *vlan = netdev_priv(dev); |
| 1407 | + int i; |
| 1408 | + struct nlattr *nest; |
| 1128 | 1409 | |
| 1129 | 1410 | if (nla_put_u32(skb, IFLA_MACVLAN_MODE, vlan->mode)) |
| 1130 | 1411 | goto nla_put_failure; |
| 1131 | 1412 | if (nla_put_u16(skb, IFLA_MACVLAN_FLAGS, vlan->flags)) |
| 1132 | 1413 | goto nla_put_failure; |
| 1414 | + if (nla_put_u32(skb, IFLA_MACVLAN_MACADDR_COUNT, vlan->macaddr_count)) |
| 1415 | + goto nla_put_failure; |
| 1416 | + if (vlan->macaddr_count > 0) { |
| 1417 | + nest = nla_nest_start(skb, IFLA_MACVLAN_MACADDR_DATA); |
| 1418 | + if (nest == NULL) |
| 1419 | + goto nla_put_failure; |
| 1420 | + |
| 1421 | + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { |
| 1422 | + if (macvlan_fill_info_macaddr(skb, vlan, i)) |
| 1423 | + goto nla_put_failure; |
| 1424 | + } |
| 1425 | + nla_nest_end(skb, nest); |
| 1426 | + } |
| 1133 | 1427 | return 0; |
| 1134 | 1428 | |
| 1135 | 1429 | nla_put_failure: |
| @@ -1139,6 +1433,10 @@ nla_put_failure: · 3 unmodified lines |
| 1139 | 1433 | static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = { |
| 1140 | 1434 | [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, |
| 1141 | 1435 | [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, |
| 1436 | + [IFLA_MACVLAN_MACADDR_MODE] = { .type = NLA_U32 }, |
| 1437 | + [IFLA_MACVLAN_MACADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, |
| 1438 | + [IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED }, |
| 1439 | + [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 }, |
| 1142 | 1440 | }; |
| 1143 | 1441 | |
| 1144 | 1442 | int macvlan_link_register(struct rtnl_link_ops *ops) |