diff options
Diffstat (limited to 'macvlan_config.c')
-rw-r--r-- | macvlan_config.c | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/macvlan_config.c b/macvlan_config.c new file mode 100644 index 0000000..5bc6723 --- /dev/null +++ b/macvlan_config.c @@ -0,0 +1,635 @@ +/* +####################################################################### +# +# (C) Copyright 2001 +# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +####################################################################### +# Notes: +# +# This configuration utility communicates with macvlan.o, the MAC address +# based VLAN support module. +# +# It uses an IOCTL interface which allows you to +# +# 1. enable/disable MAC address based VLANS over an ether type net_device +# 2. add/remove a MAC address based VLAN - which is an ether type net_device +# layered over the original MACVLAN enabled ether type net_device. +# 3. bind/unbind MAC addresses to/from particular MAC address based VLANs +# 4. discover the state of MAC address based VLANs on the system. +# 5. set/get port flags, including whether to bind to destination MAC +# or source mac. +# 6. Traffic to/from eth0 will not be affected. + + +# +# Example: (Assuming you are using source binding) +# +# If you enable MAC address based VLANS over eth0 +# +# You may then create further VLANs, e.g. eth0#1 eth0#2 .... +# These will not receive any frames until you bind MAC addresses to them. +# If you bind 11:22:33:44:55:66 to eth0#1, then any frames received by +# eth0 with source MAC 11:22:33:44:55:66 will be routed up through eth0#1 +# instead of eth0. +# +# Example: (Assuming you are using destination (local) binding) +# +# If you enable MAC address based VLANS over eth0 +# +# You may then create further VLANs, e.g. eth0#1 eth0#2 .... +# These will not receive any frames until you bind MAC addresses to them. +# If you bind 11:22:33:44:55:66 to eth0#1, then any broadcast/multicast +# frames, or frames with a destination MAC 11:22:33:44:55:66 +# will be routed up through eth0#1 instead of eth0 +# +# For broadcasts, the packet will be duplicated for every VLAN +# with at least one MAC attached. Attaching more than one MAC +# when destination binding makes no sense...don't do it! +# +# +# +####################################################################### +*/ +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/if_macvlan.h> +#include <linux/sockios.h> +#include <string.h> +#include <errno.h> + + +int do_help(int argc, char *argv[]); +int do_enable(int argc, char *argv[]); +int do_disable(int argc, char *argv[]); +int do_add(int argc, char *argv[]); +int do_del(int argc, char *argv[]); +int do_bind(int argc, char *argv[]); +int do_unbind(int argc, char *argv[]); +int do_info(int argc, char *argv[]); +int do_setflags(int argc, char *argv[]); +int do_unload(int argc, char* argv[]); + +struct command { + char *name; + char *short_help; + int (*fn)(int argc, char *argv[]); + char *long_help; +} command_list[] = { + {"help", "help on other commands", do_help, "help <command>"}, + {"enable", "enables mac based vlans over an ethernet device", do_enable, + "enable <ifname>\n" + " - enables mac based vlans over \"ifname\"\n" + " - also creates a default vlan over \"ifname\" called \"ifname#0\"" + }, + {"disable", "disables mac based vlans over an ethernet device", do_disable, "disable <ifname>"}, + {"add", "creates new mac based vlan", do_add, + "add <ifname> <index>\n" + " - creates a new mac based vlan called \"ifname#index\" layered over \"ifname\"\n" + " - mac based vlans over \"ifname\" must first be enabled with \"enable\"\n" + " - \"ifname#index\" is not mapped to any MAC address until \"bind\" is called" + }, + {"del", "destroys a mac based vlan", do_del, + "del <ifname>\n" + " - deletes a mac base vlan called \"ifname\"" + }, + {"bind", "binds macaddr to vlan", do_bind, + "bind <ifname> <macaddr>\n" + " - binds macaddr to vlan called \"ifname\"" + }, + {"unbind", "unbinds macaddr from vlan", do_unbind, + "unbind <ifname> <macaddr>\n" + " - unbinds macaddr from vlan called \"ifname\"" + }, + {"unload", "Unconfigure all of the macvlan devices", + do_unload, "Unconfigure all of the macvlan devices so module can be unloaded"}, + {"setflags", "Set port flags on a port", + do_setflags, + "setflags <ifname> <new_flags>\n" + "0x01 Bind to Destination instead of source MAC" + }, + {"info", "print state of mac based vlans", do_info, "info"}, +}; +#define NCOMMANDS (sizeof(command_list)/sizeof(struct command)) + + +int parseInt(const char* s) { + return strtol(s, NULL, 0); //should parse HEX, Octal, and Decimal. If not decimal, must start with 0x +} + + +int do_help(int argc, char *argv[]) +{ + unsigned int cmd; + if (argc < 2) + return -1; + + for (cmd = 0; cmd < NCOMMANDS; cmd++) { + if (!strcmp(command_list[cmd].name,argv[1])) + break; + } + if (cmd == NCOMMANDS) + return -1; + puts(command_list[cmd].long_help); + return 0; +} + +int do_enable(int argc, char *argv[]) +{ + struct macvlan_ioctl req; + int s; + + if (argc < 2) { + printf("usage: %s <ifname>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + req.cmd = MACVLAN_ENABLE; + req.ifname = argv[1]; /* + * name of ethernet device over which we + * are enabling mac based vlans + */ + + if (ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + if (errno != EEXIST) { + perror("ioctl (SIOCGIFMACVLAN, MACVLAN_ENABLE)"); + printf("errno: %i\n", errno); + return 1; + } + else { + return 0; + } + } + return 0; +} + + +int do_setflags(int argc, char *argv[]) +{ + struct macvlan_ioctl req; + int s; + + if (argc < 3) { + printf("usage: %s <ifname> <flags>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + req.cmd = MACVLAN_SET_PORT_FLAGS; + req.ifname = argv[1]; /* + * name of ethernet device over which we + * are enabling mac based vlans + */ + req.ifidx = parseInt(argv[2]); + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (SIOCGIFMACVLAN, SET_PORT_FLAGS)"); + return 1; + } + return 0; +} + +int _do_disable(char* port, int s) { + struct macvlan_ioctl req; + + req.cmd = MACVLAN_DISABLE; + req.ifname = port; /* + * name of ethernet device over which we + * are disabling mac based vlans + */ + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("disable-port"); + return -1; + } + else { + printf("Disabled port: %s\n", port); + } + return 0; +} + +int do_disable(int argc, char *argv[]) +{ + int s; + + if (argc < 2) { + printf("usage: %s <ifname>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + return _do_disable(argv[1], s); +} + +int do_add(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + + if (argc < 3) { + printf("usage: %s <ifname> <index>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + req.cmd = MACVLAN_ADD; + req.ifname = argv[1]; /* name of lower layer i/f over which we are adding an upper layer i/f */ + req.ifidx = parseInt(argv[2]); + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (SIOCGIFMACVLAN, MACVLAN_ADD)"); + return 1; + } + return 0; +} + +int _do_del(char* ifname, int s) { + struct macvlan_ioctl req; + + req.cmd = MACVLAN_DEL; + req.ifname = ifname; /* name mac based vlan to destroy */ + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + printf("failed to delete interface: %s\n", ifname); + perror("ioctl (SIOCGIFMACVLAN, MACVLAN_DEL)"); + return -1; + } + else { + printf("Deleted interface: %s\n", ifname); + } + + return 0; +} + +int do_del(int argc, char *argv[]) +{ + int s; + + if (argc < 2) { + printf("usage: %s <ifname>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + return _do_del(argv[1], s); +} + + + +int get_num_ports(int s) { + struct macvlan_ioctl req; + struct macvlan_ioctl_reply rep; + + req.cmd = MACVLAN_GET_NUM_PORTS; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (SIOCGIFMACVLAN, GET_NUM_PORTS)"); + return -1; + } + + printf("Found: %i ports\n", rep.num); + + return rep.num; +} + +int get_num_vlans(int portidx, int s) { + struct macvlan_ioctl req; + struct macvlan_ioctl_reply rep; + + /* Get the number of mac-based-vlans layered + * over this ethernet device + */ + req.cmd = MACVLAN_GET_NUM_VLANS; + req.portidx = portidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_VLANS)"); + return -1; + } + printf("Found: %i vlans for port: %i\n", rep.num, portidx); + return rep.num; +} + + +int htoi(char *s) +{ + char ch; + int i = 0; + while ((ch = *s++)) { + i <<= 4; + i += (ch>='0'&&ch<='9')?(ch-'0'):((ch>='a'&&ch<='f')?(ch-'a'+10):((ch>='A'&&ch<='F')?(ch-'A'+10):0)); + } + return i; +} + +int do_bind(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + char *ptr; + unsigned char macaddr[6]; + + if (argc < 3) { + printf("usage: %s <ifname> <macaddr>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + req.cmd = MACVLAN_BIND; + req.ifname = argv[1]; /* name of vlan to which we are binding a MAC address */ + + /* assemble the macaddr */ + ptr = argv[2]; + if (strlen(ptr) != 17) { + printf("bad macaddr format: need aa:bb:cc:dd:ee:ff\n"); + return 1; + } + for (ptr = argv[2]+2; ptr < argv[2]+16; ptr+=3) + *ptr = 0; + ptr = argv[2]; + macaddr[0] = (unsigned char)htoi(ptr); + macaddr[1] = (unsigned char)htoi(ptr+3); + macaddr[2] = (unsigned char)htoi(ptr+6); + macaddr[3] = (unsigned char)htoi(ptr+9); + macaddr[4] = (unsigned char)htoi(ptr+12); + macaddr[5] = (unsigned char)htoi(ptr+15); + req.macaddr = macaddr; + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (MACVLAN_BIND)"); + return 1; + } + return 0; +} + +int do_unbind(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + char *ptr; + unsigned char macaddr[6]; + + if (argc < 3) { + printf("usage: %s <ifname> <macaddr>\n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + req.cmd = MACVLAN_UNBIND; + req.ifname = argv[1]; /* name of vlan from which we are deleting a MAC address */ + + /* assemble the macaddr */ + ptr = argv[2]; + if (strlen(ptr) != 17) { + printf("bad macaddr format: need aa:bb:cc:dd:ee:ff\n"); + return 1; + } + for (ptr = argv[2]+2; ptr < argv[2]+16; ptr+=3) + *ptr = 0; + ptr = argv[2]; + macaddr[0] = (unsigned char)htoi(ptr); + macaddr[1] = (unsigned char)htoi(ptr+3); + macaddr[2] = (unsigned char)htoi(ptr+6); + macaddr[3] = (unsigned char)htoi(ptr+9); + macaddr[4] = (unsigned char)htoi(ptr+12); + macaddr[5] = (unsigned char)htoi(ptr+15); + req.macaddr = macaddr; + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (MACVLAN_UNBIND)"); + return 1; + } + return 0; +} + +int do_info(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + struct macvlan_ioctl_reply rep; + int nports; + int portidx; + int nifs; + int ifidx; + int nmacs; + int macidx; + unsigned char *p; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + /* get the number of ethernet devices which have mac based vlans + * enabled over them + */ + req.cmd = MACVLAN_GET_NUM_PORTS; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_PORTS)"); + return 1; + } + nports = rep.num; + for (portidx = 0; portidx < nports; portidx++) { + char tmpifname[64]; + /* Get the name of this mac-based-vlan enabled + * ethernet device + */ + req.cmd = MACVLAN_GET_PORT_NAME; + req.portidx = portidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_PORT_NAME)"); + return 1; + } + printf("-%s\n", rep.name); + + /* get the port flags */ + req.cmd = MACVLAN_GET_PORT_FLAGS; + req.portidx = portidx; + strcpy(tmpifname, rep.name); + req.ifname = tmpifname; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_PORT_FLAGS)"); + return 1; + } + printf("-%s flag: 0x%x\n", tmpifname, rep.num); + + /* Get the number of mac-based-vlans layered + * over this ethernet device + */ + req.cmd = MACVLAN_GET_NUM_VLANS; + req.portidx = portidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_VLANS)"); + return 1; + } + nifs = rep.num; + for (ifidx = 0; ifidx < nifs; ifidx++) { + /* Get the name of this vlan */ + req.cmd = MACVLAN_GET_VLAN_NAME; + req.portidx = portidx; + req.ifidx = ifidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_VLAN_NAME)"); + return 1; + } + /* get the number of mac addresses owned by this vlan */ + printf(" |-%s\n", rep.name); + req.cmd = MACVLAN_GET_NUM_MACS; + req.portidx = portidx; + req.ifidx = ifidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_MACS)"); + return 1; + } + nmacs = rep.num; + for (macidx = 0; macidx < nmacs; macidx++) { + /* get the value of this mac address */ + req.cmd = MACVLAN_GET_MAC_NAME; + req.portidx = portidx; + req.ifidx = ifidx; + req.macaddridx = macidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_MAC_NAME)"); + return 1; + } + p = (unsigned char *) rep.name; + printf(" | |-%02x:%02x:%02x:%02x:%02x:%02x\n", + p[0],p[1],p[2],p[3],p[4],p[5]); + } + } + } + return 0; +} + + +int do_unload(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + struct macvlan_ioctl_reply rep; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + while (get_num_ports(s) > 0) { + char port[64]; + /* Get the name of this mac-based-vlan enabled + * ethernet device + */ + req.cmd = MACVLAN_GET_PORT_NAME; + req.portidx = 0; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_PORT_NAME)"); + return 1; + } + strcpy(port, rep.name); + + while (get_num_vlans(0, s) > 0) { + char cmd[128]; + /* Get the name of this vlan */ + req.cmd = MACVLAN_GET_VLAN_NAME; + req.portidx = 0; + req.ifidx = 0; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_VLAN_NAME)"); + return 1; + } + + /* Configure down the vlan */ + /* This would be faster using IOCTLs, of course! */ + printf("Configuring down interface: %s with ifconfig...", rep.name); + sprintf(cmd, "ifconfig %s down", rep.name); + system(cmd); + + /* Now, can remove it */ + _do_del(rep.name, s); + } + + /* Now, remove the port */ + _do_disable(port, s); + + } + return 0; +} + +int main(int argc, char *argv[]) +{ + unsigned int cmd; + int err; + + if (argc < 2) + goto usage; + + for (cmd = 0; cmd < NCOMMANDS; cmd++) { + if (!strcmp(command_list[cmd].name,argv[1])) + break; + } + if (cmd == NCOMMANDS) + goto usage; + + if ((err = command_list[cmd].fn(argc-1,argv+1))) + goto usage; + return 0; + + usage: + printf("\n%s subcommands:\n\n", argv[0]); + for (cmd = 0; cmd < NCOMMANDS; cmd++) { + printf("%s %s:\t%s\n",argv[0],command_list[cmd].name,command_list[cmd].short_help); + } + return err; +} |