├── etc └── netplugd.conf ├── .gitignore ├── ChangeLog ├── docs ├── Makefile └── state-machine.dot ├── NEWS ├── TODO ├── scripts ├── netplug └── rc.netplugd ├── Makefile ├── netplug.spec ├── netplug.h ├── README ├── config.c ├── lib.c ├── man └── man8 │ └── netplugd.8 ├── netlink.c ├── main.c ├── if_info.c └── COPYING /etc/netplugd.conf: -------------------------------------------------------------------------------- 1 | eth* 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*\.o$ 2 | .*~$ 3 | netplugd$ 4 | rpm/.* 5 | netplug-.*\.tar\.bz2$ 6 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2003-08-27 Bryan O'Sullivan 2 | 3 | * Release 1.0. 4 | 5 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | all: state-machine.ps 2 | 3 | %.ps: %.dot 4 | dot -Tps -o $@ $< 5 | 6 | %.eps: %.ps 7 | ps2epsi $< $@ 8 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Changes from 1.2.8 to 1.2.9 2 | 3 | * Numerous minor, but long-delayed, documentation and build changes. 4 | 5 | * Removed balky code from RPM spec file that attempted to modify 6 | network interface configurations. 7 | 8 | Changes from 1.2.7 to 1.2.8 9 | 10 | * Fixed longstanding bug in writing out pid file. 11 | 12 | * Synced with downstream changes from Fedora Core 3. 13 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Stuff that needs doing: 2 | 3 | - Prevent food fights with hotplug and/or pcmcia_cs over who ought to 4 | be trying to configure interfaces when PCMCIA or Cardbus network 5 | cards are inserted. 6 | 7 | - Deal with wireless interfaces, which are currently untested. 8 | 9 | - Integrate with distributions other than Red Hat. I don't use any 10 | others, so patches are welcome. 11 | 12 | - Add a notification interface, so other programs that care about 13 | carrier events can talk to netplug, instead of using the rather 14 | obtuse netlink protocol. 15 | 16 | - Avoid storms of DHCP traffic if a cluster of systems running netplug 17 | is connected to a big switch that gets power cycled. This will 18 | probably involve adding a smallish random delay before responding to 19 | a netlink event. 20 | 21 | - See if anything special needs doing for interfaces that don't look 22 | like Ethernet. 23 | -------------------------------------------------------------------------------- /docs/state-machine.dot: -------------------------------------------------------------------------------- 1 | /* -*- c -*- */ 2 | 3 | digraph state_machine { 4 | insane [label="INSANE"]; 5 | probing [label="PROBING"]; 6 | down [label="DOWN"]; 7 | inactive [label="INACTIVE"]; 8 | inning [label="INNING"]; 9 | wait_in [label="WAIT_IN"]; 10 | outing [label="OUTING"]; 11 | downandout [label="DOWNANDOUT"]; 12 | active [label="ACTIVE"]; 13 | probing_up [label="PROBING_UP"]; 14 | 15 | insane -> probing [label="regained\nsanity"]; 16 | 17 | probing -> down [label="probe\ndone"]; 18 | probing -> probing_up [label="up -> 1"]; 19 | 20 | down -> inactive [label="up -> 1"]; 21 | 22 | probing_up -> inactive [label="probe\ndone"]; 23 | probing_up -> probing [label="up -> 0"]; 24 | 25 | inactive -> inning [label="running -> 1"]; 26 | inactive -> probing [label="up -> 0"]; 27 | 28 | inning -> wait_in [label="running -> 0"]; 29 | inning -> active [label="in done OK"]; 30 | 31 | wait_in -> outing [label="in done"]; 32 | 33 | active -> outing [label="running -> 0"]; 34 | 35 | outing -> downandout [label="up -> 0"]; 36 | outing -> inactive [label="out done"]; 37 | 38 | downandout -> probing [label="out done"]; 39 | } 40 | -------------------------------------------------------------------------------- /scripts/netplug: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # netplug - policy agent for netplugd 4 | # 5 | # Copyright 2003 Key Research, Inc. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License, version 2, as 9 | # published by the Free Software Foundation. You are forbidden from 10 | # redistributing or modifying it under the terms of any other license, 11 | # including other versions of the GNU General Public License. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | 18 | 19 | PATH=/usr/bin:/bin:/usr/sbin:/sbin 20 | export PATH 21 | 22 | dev="$1" 23 | action="$2" 24 | 25 | case "$action" in 26 | in) 27 | if [ -x /sbin/ifup ]; then 28 | exec /sbin/ifup $dev 29 | else 30 | echo "Please teach me how to plug in an interface!" 1>&2 31 | exit 1 32 | fi 33 | ;; 34 | out) 35 | if [ -x /sbin/ifdown ]; then 36 | # At least on Fedora Core 1, the call to ip addr flush infloops 37 | # /sbin/ifdown $dev && exec /sbin/ip addr flush $dev 38 | exec /sbin/ifdown $dev 39 | else 40 | echo "Please teach me how to unplug an interface!" 1>&2 41 | exit 1 42 | fi 43 | ;; 44 | probe) 45 | exec /sbin/ip link set $dev up >/dev/null 2>&1 46 | ;; 47 | *) 48 | echo "I have been called with a funny action of '%s'!" 1>&2 49 | exit 1 50 | ;; 51 | esac 52 | -------------------------------------------------------------------------------- /scripts/rc.netplugd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # netplugd This shell script takes care of starting and stopping 4 | # the network plug management daemon. 5 | # 6 | # chkconfig: - 11 89 7 | # description: netplugd is a daemon for managing non-static network \ 8 | # interfaces. 9 | # processname: netplugd 10 | # pidfile: /var/run/netplugd.pid 11 | 12 | # Copyright 2003 Key Research, Inc. 13 | 14 | # Source function library. 15 | . /etc/rc.d/init.d/functions 16 | 17 | # Source networking configuration. 18 | . /etc/sysconfig/network 19 | 20 | # Check that networking is up. 21 | [ ${NETWORKING} = "no" ] && exit 0 22 | 23 | [ -x /sbin/netplugd ] || exit 0 24 | 25 | if [ -f /etc/sysconfig/netplugd ]; then 26 | . /etc/sysconfig/netplugd 27 | fi 28 | 29 | # See how we were called. 30 | case "$1" in 31 | start) 32 | # Start daemon. 33 | echo -n $"Starting network plug daemon: " 34 | daemon /sbin/netplugd ${NETPLUGDARGS} -p /var/run/netplugd.pid 35 | RETVAL=$? 36 | echo 37 | [ $RETVAL -eq 0 ] && touch /var/lock/subsys/netplugd 38 | ;; 39 | stop) 40 | # Stop daemon. 41 | echo -n $"Shutting down network plug daemon: " 42 | killproc netplugd 43 | RETVAL=$? 44 | echo 45 | [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/netplugd 46 | ;; 47 | status) 48 | status netplugd 49 | RETVAL=$? 50 | ;; 51 | restart|reload) 52 | $0 stop 53 | $0 start 54 | ;; 55 | condrestart) 56 | [ -f /var/lock/subsys/netplugd ] && restart || : 57 | ;; 58 | *) 59 | echo $"Usage: $0 {start|stop|status|restart}" 60 | RETVAL=1 61 | ;; 62 | esac 63 | 64 | exit $RETVAL 65 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | version := $(shell awk '/define version/{print $$3}' netplug.spec) 2 | 3 | DESTDIR ?= 4 | 5 | prefix ?= 6 | bindir ?= $(prefix)/sbin 7 | etcdir ?= $(prefix)/etc/netplug 8 | initdir ?= $(prefix)/etc/rc.d/init.d 9 | scriptdir ?= $(prefix)/etc/netplug.d 10 | mandir ?= $(prefix)/usr/share/man 11 | 12 | install_opts := 13 | 14 | CFLAGS += -Wall -std=gnu99 -DNP_ETC_DIR='"$(etcdir)"' \ 15 | -DNP_SCRIPT_DIR='"$(scriptdir)"' -ggdb3 -O3 -DNP_VERSION='"$(version)"' 16 | 17 | netplugd: config.o netlink.o lib.o if_info.o main.o 18 | $(CC) $(LDFLAGS) -o $@ $^ 19 | 20 | install: 21 | install -d $(install_opts) -m 755 \ 22 | $(DESTDIR)/$(bindir) \ 23 | $(DESTDIR)/$(etcdir) \ 24 | $(DESTDIR)/$(scriptdir) \ 25 | $(DESTDIR)/$(initdir) \ 26 | $(DESTDIR)/$(mandir)/man8 27 | install $(install_opts) -m 755 netplugd $(DESTDIR)/$(bindir) 28 | install $(install_opts) -m 644 etc/netplugd.conf $(DESTDIR)/$(etcdir) 29 | install $(install_opts) -m 755 scripts/netplug $(DESTDIR)/$(scriptdir) 30 | install $(install_opts) -m 755 scripts/rc.netplugd $(DESTDIR)/$(initdir)/netplugd 31 | install $(install_opts) -m 444 man/man8/netplugd.8 $(DESTDIR)/$(mandir)/man8 32 | 33 | hg_root := $(shell hg root) 34 | tar_root := netplug-$(version) 35 | tar_file := $(hg_root)/$(tar_root).tar.bz2 36 | files := $(shell hg manifest) 37 | 38 | tarball: $(tar_file) 39 | 40 | $(tar_file): $(files) 41 | mkdir -p $(hg_root)/$(tar_root) 42 | echo $(files) | tr ' ' '\n' | \ 43 | xargs -i cp -a --parents {} $(hg_root)/$(tar_root) 44 | tar -C $(hg_root) -c -f - $(tar_root) | bzip2 -9 > $(tar_file) 45 | rm -rf $(hg_root)/$(tar_root) 46 | 47 | clean: 48 | -rm -f netplugd *.o *.tar.bz2 49 | -------------------------------------------------------------------------------- /netplug.spec: -------------------------------------------------------------------------------- 1 | %define version 1.2.9.2 2 | %define release 1 3 | %define sysconfig %{_sysconfdir}/sysconfig/network-scripts 4 | 5 | Summary: Daemon that responds to network cables being plugged in and out 6 | Name: netplug 7 | Version: %{version} 8 | Release: %{release} 9 | License: GPL 10 | Group: System Environment/Base 11 | URL: http://www.red-bean.com/~bos/ 12 | Packager: Bryan O'Sullivan 13 | Vendor: PathScale, Inc. 14 | Source: %{name}-%{version}.tar.bz2 15 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot 16 | Requires: iproute >= 2.4.7 17 | 18 | %description 19 | Netplug is a daemon that manages network interfaces in response to 20 | link-level events such as cables being plugged in and out. When a 21 | cable is plugged into an interface, the netplug daemon brings that 22 | interface up. When the cable is unplugged, the daemon brings that 23 | interface back down. 24 | 25 | This is extremely useful for systems such as laptops, which are 26 | constantly being unplugged from one network and plugged into another, 27 | and for moving systems in a machine room from one switch to another 28 | without a need for manual intervention. 29 | 30 | %prep 31 | %setup -q 32 | 33 | %build 34 | make 35 | 36 | %install 37 | rm -rf $RPM_BUILD_ROOT 38 | make install prefix=$RPM_BUILD_ROOT \ 39 | initdir=$RPM_BUILD_ROOT/%{_initrddir} \ 40 | mandir=$RPM_BUILD_ROOT/%{_mandir} 41 | 42 | %clean 43 | rm -rf $RPM_BUILD_ROOT 44 | 45 | %files 46 | %defattr(-,root,root,-) 47 | %config %{_sysconfdir}/netplug/netplugd.conf 48 | %{_sysconfdir}/netplug.d 49 | %{_sysconfdir}/netplug 50 | %{_initrddir}/netplugd 51 | /sbin/netplugd 52 | %{_mandir}/man*/* 53 | 54 | %doc COPYING ChangeLog NEWS README TODO 55 | 56 | %post 57 | /sbin/chkconfig --add netplugd 58 | 59 | %preun 60 | /sbin/chkconfig --del netplugd 61 | 62 | %changelog 63 | * Tue Aug 26 2003 Bryan O'Sullivan - 64 | - Initial build. 65 | -------------------------------------------------------------------------------- /netplug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * netplug.h - common include file 3 | * 4 | * Copyright 2003 PathScale, Inc. 5 | * Copyright 2003, 2004, 2005 Bryan O'Sullivan 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 Foundation. You are 10 | * forbidden from redistributing or modifying it under the terms of 11 | * any other license, including other versions of the GNU General 12 | * Public License. 13 | * 14 | * This program is distributed in the hope that it will be useful, but 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * General Public License for more details. 18 | */ 19 | 20 | #ifndef __netplug_h 21 | #define __netplug_h 22 | 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* configuration */ 30 | 31 | void read_config(char *filename); 32 | int save_pattern(char *pat); 33 | int if_match(char *iface); 34 | int try_probe(char *iface); 35 | void probe_interfaces(void); 36 | void close_on_exec(int fd); 37 | 38 | extern const char *script_file; 39 | 40 | extern int debug; 41 | 42 | /* netlink interfacing */ 43 | 44 | typedef int (*netlink_callback)(struct nlmsghdr *hdr, void *arg); 45 | 46 | int netlink_open(void); 47 | void netlink_request_dump(int fd); 48 | void netlink_receive_dump(int fd, netlink_callback callback, void *arg); 49 | int netlink_listen(int fd, netlink_callback callback, void *arg); 50 | 51 | 52 | /* network interface info management */ 53 | 54 | struct if_info { 55 | struct if_info *next; 56 | int index; 57 | int type; 58 | unsigned flags; 59 | int addr_len; 60 | unsigned char addr[8]; 61 | char name[16]; 62 | 63 | enum ifstate { 64 | ST_DOWN, /* uninitialized */ 65 | ST_DOWNANDOUT, /* went down while running out script */ 66 | ST_PROBING, /* running probe script */ 67 | ST_PROBING_UP, /* running probe, and interface went UP */ 68 | ST_INACTIVE, /* interface inactive */ 69 | ST_INNING, /* plugin script is running */ 70 | ST_WAIT_IN, /* wait until plugin script is done */ 71 | ST_ACTIVE, /* interface active */ 72 | ST_OUTING, /* plugout script is running */ 73 | ST_INSANE, /* interface seems to be flapping */ 74 | } state; 75 | 76 | pid_t worker; /* pid of current in/out script */ 77 | time_t lastchange; /* timestamp of last state change */ 78 | }; 79 | 80 | struct if_info *if_info_get_interface(struct nlmsghdr *hdr, 81 | struct rtattr *attrs[]); 82 | struct if_info *if_info_update_interface(struct nlmsghdr *hdr, 83 | struct rtattr *attrs[]); 84 | int if_info_save_interface(struct nlmsghdr *hdr, void *arg); 85 | void parse_rtattrs(struct rtattr *tb[], int max, struct rtattr *rta, int len); 86 | void for_each_iface(int (*func)(struct if_info *)); 87 | 88 | void ifsm_flagpoll(struct if_info *info); 89 | void ifsm_flagchange(struct if_info *info, unsigned int newflags); 90 | void ifsm_scriptdone(pid_t pid, int exitstatus); 91 | 92 | /* utilities */ 93 | 94 | void do_log(int pri, const char *fmt, ...) 95 | __attribute__ ((format (printf, 2, 3))); 96 | pid_t run_netplug_bg(char *ifname, char *action); 97 | int run_netplug(char *ifname, char *action); 98 | void kill_script(pid_t pid); 99 | void *xmalloc(size_t n); 100 | 101 | 102 | #endif /* __netplug_h */ 103 | 104 | 105 | /* 106 | * Local variables: 107 | * c-file-style: "stroustrup" 108 | * End: 109 | */ 110 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is the README file for netplug, a Linux daemon that responds to 2 | network link events, such as a network interface losing or acquiring a 3 | carrier signal. 4 | 5 | You can use netplug to configure a network interface automatically 6 | when you plug in a live network cable, and unconfigure the interface 7 | when you unplug the cable. 8 | 9 | Netplug is licensed under the GNU General Public License, version 2. 10 | Copyright 2003 Pathscale, Inc. 11 | Copyright 2003, 2004, 2005 Bryan O'Sullivan 12 | 13 | 14 | What is netplug? 15 | ---------------- 16 | 17 | Linux already supports plugging in and configuring the likes of 18 | Cardbus network interfaces, in the form of the hotplug subsystem. 19 | Netplug provides the corresponding support for plugging and unplugging 20 | cables into network interfaces. 21 | 22 | When an Ethernet-style network interface on a host is plugged into a 23 | powered-up switch, hub, or other host, the two use a carrier signal to 24 | establish that the link is alive. The Linux kernel makes this 25 | information available through its netlink interface. 26 | 27 | The netplug daemon listens for carrier detection and loss messages 28 | from the kernel's netlink subsystem. When a carrier signal is 29 | detected on an interface, it runs a script to bring the interface up. 30 | When carrier is lost, netplug runs a script to bring the interface 31 | down. 32 | 33 | 34 | Why use netplug? 35 | ---------------- 36 | 37 | If you use static IP addresses, or your host is always plugged into 38 | the same port on the same network, there's not much benefit to using 39 | netplug. Netplug comes into its own in two situations: 40 | 41 | - If you're a laptop user who migrates from one network to another, 42 | you can use netplug to automate the management of your network 43 | interface as you unplug from one network and plug into another. 44 | Without netplug, you are forced to manually run commands such as 45 | "/sbin/ifup eth0" when you plug into a new network. 46 | 47 | - If you have a large computing cluster, you may periodically need to 48 | move hosts around to balance the loads among a collection of 49 | switches and file servers. If you only need to move network 50 | cabling, you can leave a host running, with its interfaces managed 51 | by netplug, and netplug will reconfigure its interfaces when you 52 | move the host from a busy switch to a less lightly loaded one. 53 | 54 | 55 | Who ships netplug? 56 | ------------------ 57 | 58 | Netplug is distributed as part of the standard net-tools package with 59 | the following Linux distributions: 60 | 61 | Fedora Core 2 and above 62 | Mandrake 10.1 and above 63 | Red Hat Enterprise 4 and above 64 | 65 | There are also packages available for Debian and Gentoo. 66 | 67 | 68 | Adapting netplug for different situations 69 | ----------------------------------------- 70 | 71 | The netplug daemon has no notion of policy or integration with a 72 | particular Linux distribution. Those are abstracted out into a 73 | script, /etc/netplug.d/netplug. This script is responsible for the 74 | distro-dependent mechanics of: 75 | 76 | - Probing for interfaces to manage 77 | 78 | - Bringing an interface up, in response to carrier acquisition 79 | 80 | - Bringing an interface down, in response to loss of carrier 81 | 82 | 83 | Helping out 84 | ----------- 85 | 86 | Patches to integrate with different Linux distributions, add features, 87 | fix bugs, add documentation, and so on, are all welcome, as are 88 | suggestions for improvements. 89 | 90 | Master Mercurial repository: 91 | 92 | hg clone http://hg.serpentine.com/netplug 93 | 94 | Contact 95 | ------- 96 | 97 | The author of netplug is Bryan O'Sullivan . 98 | 99 | 100 | Thanks 101 | ------ 102 | 103 | Thanks to the following people for their excellent work in making 104 | netplug more robust: 105 | 106 | Jeremy Fitzhardinge 107 | Steve Grubb 108 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * config.c - manage configuration data 3 | * 4 | * Copyright 2003 PathScale, Inc. 5 | * Copyright 2003, 2004, 2005 Bryan O'Sullivan 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 Foundation. You are 10 | * forbidden from redistributing or modifying it under the terms of 11 | * any other license, including other versions of the GNU General 12 | * Public License. 13 | * 14 | * This program is distributed in the hope that it will be useful, but 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * General Public License for more details. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "netplug.h" 29 | 30 | 31 | struct if_pat { 32 | char *pat; 33 | struct if_pat *next; 34 | }; 35 | 36 | static struct if_pat *pats; 37 | static struct if_pat *memo; 38 | 39 | 40 | int 41 | if_match(char *name) 42 | { 43 | struct if_pat *pat; 44 | 45 | if (memo && fnmatch(memo->pat, name, 0) == 0) { 46 | return 1; 47 | } 48 | 49 | for (pat = pats; pat != NULL; pat = pat->next) { 50 | if (fnmatch(pat->pat, name, 0) == 0) { 51 | memo = pat; 52 | return 1; 53 | } 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | 60 | int 61 | save_pattern(char *name) 62 | { 63 | int len = strlen(name); 64 | 65 | if (len == 0) { 66 | return 0; 67 | } 68 | 69 | int x = fnmatch(name, "eth0", 0); 70 | 71 | if (x != 0 && x != FNM_NOMATCH) { 72 | return -1; 73 | } 74 | 75 | struct if_pat *pat = xmalloc(sizeof(*pat)); 76 | 77 | pat->pat = xmalloc(len + 1); 78 | memcpy(pat->pat, name, len + 1); 79 | pat->next = pats; 80 | pats = pat; 81 | 82 | return 0; 83 | } 84 | 85 | 86 | void 87 | read_config(char *filename) 88 | { 89 | FILE *fp; 90 | 91 | if (filename == NULL || strcmp(filename, "-") == 0) { 92 | filename = "stdin"; 93 | fp = stdin; 94 | } else if ((fp = fopen(filename, "r")) == NULL) { 95 | do_log(LOG_ERR, "%s: %m", filename); 96 | return; 97 | } 98 | 99 | char buf[8192]; 100 | 101 | for (int line = 1; fgets(buf, sizeof(buf), fp); line++) { 102 | char *l, *r; 103 | 104 | for (l = buf; *l != '\0' && isspace(*l); l++) { 105 | } 106 | for (r = l; *r != '\0' && !isspace(*r); r++) { 107 | } 108 | 109 | *r = '\0'; 110 | 111 | char *h; 112 | 113 | if ((h = strchr(l, '#')) != NULL) { 114 | *h = '\0'; 115 | } 116 | 117 | if (save_pattern(l) == -1) { 118 | do_log(LOG_ERR, "%s, line %d: bad pattern: %s", 119 | filename, line, l); 120 | exit(1); 121 | } 122 | } 123 | 124 | if (ferror(fp)) { 125 | do_log(LOG_ERR, "%s: %m", filename); 126 | } 127 | 128 | if (fp != stdin) { 129 | fclose(fp); 130 | } 131 | } 132 | 133 | 134 | static int 135 | has_meta(char *s) 136 | { 137 | static const char meta[] = "[]*?"; 138 | 139 | for (char *x = s; *x != '\0'; x++) { 140 | for (const char *m = meta; *m != '\0'; m++) { 141 | if (*x == *m) { 142 | return x - s; 143 | } 144 | } 145 | } 146 | 147 | return -1; 148 | } 149 | 150 | 151 | int 152 | try_probe(char *iface) 153 | { 154 | return run_netplug(iface, "probe") == 0 ? 1 : 0; 155 | } 156 | 157 | 158 | void 159 | probe_interfaces(void) 160 | { 161 | int nmatch = 0; 162 | 163 | for (struct if_pat *p = pats; p != NULL; p = p->next) { 164 | int m; 165 | 166 | if ((m = has_meta(p->pat)) == -1) { 167 | nmatch += try_probe(p->pat); 168 | } 169 | else if (m == 0) { 170 | do_log(LOG_WARNING, "Don't know how to probe for interfaces " 171 | "matching %s", p->pat); 172 | continue; 173 | } 174 | else { 175 | char *z = xmalloc(m + 4); 176 | 177 | strncpy(z, p->pat, m); 178 | 179 | for (int i = 0; i < 16; i++) { 180 | sprintf(z + m, "%d", i); 181 | if (fnmatch(p->pat, z, 0) == 0) { 182 | nmatch += try_probe(z); 183 | } 184 | } 185 | 186 | free(z); 187 | } 188 | } 189 | 190 | if (nmatch == 0) { 191 | do_log(LOG_WARNING, "Could not probe for any interfaces"); 192 | } 193 | } 194 | 195 | 196 | /* 197 | * Local variables: 198 | * c-file-style: "stroustrup" 199 | * End: 200 | */ 201 | -------------------------------------------------------------------------------- /lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lib.c - random library routines 3 | * 4 | * Copyright 2003 PathScale, Inc. 5 | * Copyright 2003, 2004, 2005 Bryan O'Sullivan 6 | * Copyright 2003 Jeremy Fitzhardinge 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License, 10 | * version 2, as published by the Free Software Foundation. You are 11 | * forbidden from redistributing or modifying it under the terms of 12 | * any other license, including other versions of the GNU General 13 | * Public License. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "netplug.h" 31 | 32 | const char *script_file = NP_SCRIPT_DIR "/netplug"; 33 | 34 | void 35 | do_log(int pri, const char *fmt, ...) 36 | { 37 | extern int use_syslog; 38 | va_list ap; 39 | va_start(ap, fmt); 40 | 41 | if (pri == LOG_DEBUG && !debug) 42 | return; 43 | 44 | if (use_syslog) { 45 | vsyslog(pri, fmt, ap); 46 | } else { 47 | FILE *fp; 48 | 49 | switch (pri) { 50 | case LOG_INFO: 51 | case LOG_NOTICE: 52 | case LOG_DEBUG: 53 | fp = stdout; 54 | break; 55 | default: 56 | fp = stderr; 57 | break; 58 | } 59 | 60 | switch (pri) { 61 | case LOG_WARNING: 62 | fputs("Warning: ", fp); 63 | break; 64 | case LOG_NOTICE: 65 | fputs("Notice: ", fp); 66 | break; 67 | case LOG_CRIT: 68 | case LOG_ERR: 69 | fputs("Error: ", fp); 70 | break; 71 | case LOG_INFO: 72 | case LOG_DEBUG: 73 | break; 74 | default: 75 | fprintf(fp, "Log type %d: ", pri); 76 | break; 77 | } 78 | 79 | vfprintf(fp, fmt, ap); 80 | fputc('\n', fp); 81 | } 82 | 83 | va_end(ap); 84 | } 85 | 86 | 87 | void 88 | close_on_exec(int fd) 89 | { 90 | if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { 91 | do_log(LOG_ERR, "can't set fd %d to close on exec: %m", fd); 92 | exit(1); 93 | } 94 | } 95 | 96 | 97 | pid_t 98 | run_netplug_bg(char *ifname, char *action) 99 | { 100 | pid_t pid; 101 | 102 | if ((pid = fork()) == -1) { 103 | do_log(LOG_ERR, "fork: %m"); 104 | exit(1); 105 | } 106 | else if (pid != 0) { 107 | return pid; 108 | } 109 | 110 | setpgrp(); /* become group leader */ 111 | 112 | do_log(LOG_INFO, "%s %s %s -> pid %d", 113 | script_file, ifname, action, getpid()); 114 | 115 | execl(script_file, script_file, ifname, action, NULL); 116 | 117 | do_log(LOG_ERR, "%s: %m", script_file); 118 | exit(1); 119 | } 120 | 121 | 122 | int 123 | run_netplug(char *ifname, char *action) 124 | { 125 | pid_t pid = run_netplug_bg(ifname, action); 126 | int status, ret; 127 | 128 | if ((ret = waitpid(pid, &status, 0)) == -1) { 129 | do_log(LOG_ERR, "waitpid: %m"); 130 | exit(1); 131 | } 132 | 133 | return WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status); 134 | } 135 | 136 | 137 | /* 138 | Synchronously kill a script 139 | 140 | Assumes the pid is actually a leader of a group. Kills first with 141 | SIGTERM at first; if that doesn't work, follow up with a SIGKILL. 142 | */ 143 | void 144 | kill_script(pid_t pid) 145 | { 146 | pid_t ret; 147 | int status; 148 | sigset_t mask, origmask; 149 | 150 | if (pid == -1) 151 | return; 152 | 153 | assert(pid > 0); 154 | 155 | /* Block SIGCHLD while we go around killing things, so the SIGCHLD 156 | handler doesn't steal things behind our back. */ 157 | sigemptyset(&mask); 158 | sigaddset(&mask, SIGCHLD); 159 | sigprocmask(SIG_BLOCK, &mask, &origmask); 160 | 161 | /* ask nicely */ 162 | if (killpg(pid, SIGTERM) == -1) { 163 | do_log(LOG_ERR, "Can't kill script pgrp %d: %m", pid); 164 | goto done; 165 | } 166 | 167 | sleep(1); 168 | 169 | ret = waitpid(pid, &status, WNOHANG); 170 | 171 | if (ret == -1) { 172 | do_log(LOG_ERR, "Failed to wait for %d: %m?!", pid); 173 | goto done; 174 | } else if (ret == 0) { 175 | /* no more Mr. nice guy */ 176 | if (killpg(pid, SIGKILL) == -1) { 177 | do_log(LOG_ERR, "2nd kill %d failed: %m?!", pid); 178 | goto done; 179 | } 180 | ret = waitpid(pid, &status, 0); 181 | } 182 | 183 | assert(ret == pid); 184 | 185 | done: 186 | sigprocmask(SIG_SETMASK, &origmask, NULL); 187 | } 188 | 189 | 190 | void * 191 | xmalloc(size_t n) 192 | { 193 | void *x = malloc(n); 194 | 195 | if (n > 0 && x == NULL) { 196 | do_log(LOG_ERR, "malloc: %m"); 197 | exit(1); 198 | } 199 | 200 | return x; 201 | } 202 | 203 | 204 | void 205 | __assert_fail(const char *assertion, const char *file, 206 | unsigned int line, const char *function) 207 | { 208 | do_log(LOG_CRIT, "%s:%u: %s%sAssertion `%s' failed", 209 | file, line, 210 | function ? function : "", 211 | function ? ": " : "", 212 | assertion); 213 | 214 | abort(); 215 | } 216 | 217 | 218 | /* 219 | * Local variables: 220 | * c-file-style: "stroustrup" 221 | * End: 222 | */ 223 | -------------------------------------------------------------------------------- /man/man8/netplugd.8: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" 3 | .\" For author, copyright, and license information, see the end of 4 | .\" this file. 5 | .\" 6 | .\" This is a -mdoc format man page. See the mdoc man page for details. 7 | .\" 8 | .Dd August 26, 2003 9 | .Dt NETPLUGD 8 10 | .Os Linux 2.6 11 | .\" 12 | .\" 13 | .Sh NAME 14 | .Nm netplugd 15 | .Nd network cable hotplug management daemon 16 | .\" 17 | .\" 18 | .Sh SYNOPSIS 19 | .Nm netplugd 20 | .Op Fl FP 21 | .Op Fl c Ar config_file 22 | .Op Fl s Ar script_file 23 | .Op Fl i Ar interface_pattern 24 | .Op Fl p Ar pid_file 25 | .\" 26 | .\" 27 | .Sh DESCRIPTION 28 | .Nm 29 | is a daemon that responds to network link events from the Linux 30 | kernel, such as a network interface losing or acquiring a carrier 31 | signal. 32 | .\" 33 | .Pp 34 | When an Ethernet-style network interface on a host is plugged into a 35 | powered-up switch, hub, or other host, the two use a carrier signal to 36 | establish that the link is alive. The Linux kernel makes this 37 | information available through its 38 | .Xr netlink 7 39 | interface. 40 | .\" 41 | .Pp 42 | The 43 | .Nm 44 | daemon listens for carrier detection and loss messages from the 45 | kernel's 46 | .Xr netlink 7 47 | subsystem. When a carrier signal is detected on an interface, it runs 48 | a script to bring the interface up. When carrier is lost, 49 | .Nm 50 | runs a script to bring the interface down. 51 | .\" 52 | .Nm 53 | does not define any policies for how to manage interfaces; it leaves 54 | that to a script, 55 | .Pa /etc/netplug.d/netplug , 56 | which is described in 57 | .Sx FILES 58 | below. 59 | .\" 60 | .Pp 61 | You tell 62 | .Nm 63 | which interfaces it should manage by giving it a list of shell-style 64 | glob patterns, which it matches against using the 65 | .Xr fnmatch 3 66 | function. For example, a pattern of 67 | .Pa eth[13] 68 | will tell 69 | .Nm 70 | to only manage 71 | .Pa eth1 72 | and 73 | .Pa eth3 , 74 | if those interfaces exist. If the interfaces are not known to the 75 | kernel at the time you start 76 | .Nm , 77 | perhaps because they are unplugged PCMCIA network interfaces or 78 | devices whose drivers have not yet been installed, 79 | .Nm 80 | will start to manage them as soon as they are plugged in or their 81 | drivers are available. 82 | .\" 83 | .\" 84 | .Sh OPTIONS 85 | .Bl -tag -width Ds 86 | .\" 87 | .It Fl F 88 | Run in the foreground; do not detach and run as a daemon. Messages 89 | are logged to 90 | .Pa stdout 91 | or 92 | .Pa stderr , 93 | instead of using the 94 | .Xr syslog 3 95 | mechanism. This option is useful mainly for debugging your 96 | configuration. 97 | .\" 98 | .It Fl P 99 | Prevent autoprobing for interfaces. The 100 | .Nm 101 | daemon normally probes for all possible interface names that might 102 | match the patterns you tell it to manage. This is necessary in order 103 | to get network driver modules (the default with almost all Linux 104 | distributions) loaded and set up, so that they can provide link status 105 | notifications to the 106 | .Nm 107 | daemon. Autoprobing should always be safe, and doesn't take long. 108 | Disable it with caution. 109 | .\" 110 | .It Fl c Ar config_file 111 | Specify the name of a file from which to read patterns that describe 112 | the interfaces to manage. You can provide this option multiple times to read 113 | from more than one file. If you do not provide this option at all, 114 | .Nm 115 | will attempt to read from a default config file. If you do not want 116 | .Nm 117 | to try to read from any real config files, you can specify 118 | .Pa /dev/null 119 | as a config file. 120 | .\" 121 | .It Fl s Ar script_file 122 | Specify an alternative script file path, override /etc/netplug.d/netplug 123 | .\" 124 | .It Fl i Ar interface_pattern 125 | Specify a pattern that will be used to match interface names that 126 | .Nm 127 | should manage. You can provide this option multiple times to specify 128 | multiple patterns. 129 | .\" 130 | .It Fl p Ar pid_file 131 | Write the daemon's process ID to the file 132 | .Ar pid_file . 133 | If you tell 134 | .Nm 135 | to run in the foreground, this option is ignored. 136 | .El 137 | .\" 138 | .\" 139 | .Sh FILES 140 | .Bl -tag -width Ds 141 | .It Pa /etc/netplug/netplugd.conf 142 | Default config file to read, if none is specified on the command line. 143 | The config file format is one pattern per line, with white space, 144 | empty lines, and comments starting with a 145 | .Li # 146 | character ignored. Patterns are standard shell-style glob patterns, 147 | e.g. "eth[0-9]". 148 | .\" 149 | .It Pa /etc/netplug.d/netplug 150 | The "policy" program (typically a shell script) that 151 | .Nm 152 | uses to probe for interfaces, and to bring them up or down in response 153 | to network link events. This program is called with the name of the 154 | interface as its first argument, and one of the following options: 155 | .Bl -tag -width Ds 156 | .It in 157 | A cable was plugged in, or carrier came up. The command should bring 158 | the interface up. The command is run asynchronously, and it should 159 | exit with status 0 on success. 160 | .It out 161 | A cable was plugged out, or carrier went down. The command should 162 | bring the interface down. The command is run asynchronously, and it should 163 | exit with status 0 on success. 164 | .It probe 165 | The command should load and initialise the driver for this interface, 166 | if possible, and bring the interface into the "up" state, so that it 167 | can generate 168 | .Xr netlink 7 169 | events. The command is run synchronously; it must exit with status 170 | code 0 if it succeeds, otherwise with a non-zero exit code or signal. 171 | .El 172 | .It Pa /etc/rc.d/init.d/netplugd 173 | The 174 | .Xr init 8 175 | script that starts, stops, and displays status of the 176 | .Nm 177 | daemon. 178 | .El 179 | .\" 180 | .\" 181 | .Sh AUTHOR 182 | .Nm 183 | was written by 184 | .An Bryan O'Sullivan 185 | .Ad . 186 | .\" 187 | .\" 188 | .Sh COPYRIGHT AND LICENSE 189 | Copyright 2003 PathScale, Inc. 190 | Copyright 2003, 2004, 2005 Bryan O'Sullivan 191 | .\" 192 | .Pp 193 | .Nm 194 | is free software; you can redistribute it and/or modify it under the 195 | terms of the GNU General Public License, version 2, as published by 196 | the Free Software Foundation. You are forbidden from redistributing 197 | or modifying it under the terms of any other license, including other 198 | versions of the GNU General Public License. 199 | .\" 200 | .Pp 201 | .Nm 202 | is distributed in the hope that it will be useful, but WITHOUT ANY 203 | WARRANTY; without even the implied warranty of MERCHANTABILITY or 204 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 205 | for more details. 206 | .\" 207 | .\" 208 | .Sh SEE ALSO 209 | .Xr cardmgr 5 , 210 | .Xr hotplug 8 , 211 | .Xr ip 8 , 212 | .Xr netlink 7 213 | -------------------------------------------------------------------------------- /netlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * netlink.c - interface with kernel's netlink facility 3 | * 4 | * Copyright 2003 PathScale, Inc. 5 | * Copyright 2003, 2004, 2005 Bryan O'Sullivan 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 Foundation. You are 10 | * forbidden from redistributing or modifying it under the terms of 11 | * any other license, including other versions of the GNU General 12 | * Public License. 13 | * 14 | * This program is distributed in the hope that it will be useful, but 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * General Public License for more details. 18 | * 19 | * Portions of this file are based on code from Alexey Kuznetsov's 20 | * iproute2 package. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "netplug.h" 30 | 31 | 32 | static int seq, dump; 33 | 34 | 35 | void 36 | netlink_request_dump(int fd) 37 | { 38 | struct { 39 | struct nlmsghdr hdr; 40 | struct rtgenmsg msg; 41 | } req; 42 | struct sockaddr_nl addr; 43 | 44 | memset(&addr, 0, sizeof(addr)); 45 | addr.nl_family = AF_NETLINK; 46 | 47 | memset(&req, 0, sizeof(req)); 48 | req.hdr.nlmsg_len = sizeof(req); 49 | req.hdr.nlmsg_type = RTM_GETLINK; 50 | req.hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; 51 | req.hdr.nlmsg_pid = 0; 52 | req.hdr.nlmsg_seq = dump = ++seq; 53 | req.msg.rtgen_family = AF_UNSPEC; 54 | 55 | if (sendto(fd, (void*) &req, sizeof(req), 0, 56 | (struct sockaddr *) &addr, sizeof(addr)) == -1) { 57 | do_log(LOG_ERR, "Could not request interface dump: %m"); 58 | exit(1); 59 | } 60 | } 61 | 62 | 63 | typedef enum { 64 | ok, /* handle the message */ 65 | skip, /* skip the message */ 66 | done, /* all's well, no more processing */ 67 | bail, /* something's wrong, no more processing */ 68 | user, /* packet came from someone naughty in user space */ 69 | } todo; 70 | 71 | 72 | static todo 73 | receive(int fd, struct msghdr *msg, int *status) 74 | { 75 | *status = recvmsg(fd, msg, 0); 76 | 77 | if (*status == -1) { 78 | if (errno == EINTR) { 79 | return skip; 80 | } 81 | if (errno == EAGAIN) { 82 | /* XXX when will this ever happen? */ 83 | return done; 84 | } 85 | 86 | do_log(LOG_ERR, "Netlink receive error: %m"); 87 | return done; 88 | } 89 | else if (*status == 0) { 90 | do_log(LOG_ERR, "Unexpected EOF on netlink"); 91 | return bail; 92 | } 93 | 94 | if (msg->msg_namelen != sizeof(struct sockaddr_nl)) { 95 | do_log(LOG_ERR, "Unexpected sender address length: got %d, expected %d", 96 | msg->msg_namelen, (int) sizeof(struct sockaddr_nl)); 97 | return done; 98 | } 99 | 100 | if (((struct sockaddr_nl *) msg->msg_name)->nl_pid != 0) { 101 | do_log(LOG_ERR, "Netlink packet came from pid %d, not from kernel", 102 | ((struct sockaddr_nl *) msg->msg_name)->nl_pid); 103 | return user; 104 | } 105 | 106 | return ok; 107 | } 108 | 109 | 110 | /* 111 | * Return values: 112 | * 113 | * 0 - exit calling loop 114 | * !0 - we have a valid event 115 | */ 116 | int 117 | netlink_listen(int fd, netlink_callback callback, void *arg) 118 | { 119 | char buf[8192]; 120 | struct iovec iov = { buf, sizeof(buf) }; 121 | struct sockaddr_nl addr; 122 | struct msghdr msg = { 123 | .msg_name = (void *) &addr, 124 | .msg_namelen = sizeof(addr), 125 | .msg_iov = &iov, 126 | .msg_iovlen = 1, 127 | }; 128 | 129 | memset(&addr, 0, sizeof(addr)); 130 | addr.nl_family = AF_NETLINK; 131 | addr.nl_pid = 0; 132 | addr.nl_groups = 0; 133 | 134 | while (1) { 135 | int status; 136 | 137 | switch (receive(fd, &msg, &status)) { 138 | case user: 139 | case done: 140 | return 1; 141 | case bail: 142 | return 0; 143 | case skip: 144 | continue; 145 | case ok: 146 | break; 147 | } 148 | 149 | struct nlmsghdr *hdr; 150 | 151 | for (hdr = (struct nlmsghdr*) buf; status >= sizeof(*hdr); ) { 152 | int len = hdr->nlmsg_len; 153 | int l = len - sizeof(*hdr); 154 | 155 | if (l < 0 || len > status) { 156 | if (msg.msg_flags & MSG_TRUNC) { 157 | do_log(LOG_ERR, "Truncated message"); 158 | return 1; 159 | } 160 | do_log(LOG_ERR, "Malformed netlink message"); 161 | return 1; 162 | } 163 | 164 | if (callback) { 165 | int err; 166 | 167 | if ((err = callback(hdr, arg)) == -1) { 168 | do_log(LOG_ERR, "Callback failed"); 169 | goto outer; 170 | } 171 | } 172 | 173 | status -= NLMSG_ALIGN(len); 174 | hdr = (struct nlmsghdr *) ((char *) hdr + NLMSG_ALIGN(len)); 175 | } 176 | if (msg.msg_flags & MSG_TRUNC) { 177 | do_log(LOG_ERR, "Message truncated"); 178 | continue; 179 | } 180 | if (status) { 181 | do_log(LOG_ERR, "!!!Remnant of size %d", status); 182 | return 1; 183 | } 184 | outer: 185 | /* do nothing */; 186 | } 187 | } 188 | 189 | 190 | void 191 | netlink_receive_dump(int fd, netlink_callback callback, void *arg) 192 | { 193 | char buf[8192]; 194 | struct sockaddr_nl addr; 195 | struct iovec iov = { buf, sizeof(buf) }; 196 | struct msghdr msg = { 197 | .msg_name = (void *) &addr, 198 | .msg_namelen = sizeof(addr), 199 | .msg_iov = &iov, 200 | .msg_iovlen = 1, 201 | }; 202 | 203 | while (1) { 204 | int status; 205 | 206 | switch (receive(fd, &msg, &status)) { 207 | case bail: 208 | case done: 209 | exit(1); 210 | case user: 211 | case skip: 212 | continue; 213 | case ok: 214 | break; 215 | } 216 | 217 | struct nlmsghdr *hdr = (struct nlmsghdr *) buf; 218 | 219 | while (NLMSG_OK(hdr, status)) { 220 | if (hdr->nlmsg_seq != dump) { 221 | do_log(LOG_ERR, "Skipping junk"); 222 | goto skip_it; 223 | } 224 | 225 | if (hdr->nlmsg_type == NLMSG_DONE) { 226 | return; 227 | } 228 | else if (hdr->nlmsg_type == NLMSG_ERROR) { 229 | struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(hdr); 230 | 231 | if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { 232 | do_log(LOG_ERR, "Netlink message truncated"); 233 | } else { 234 | errno = -err->error; 235 | do_log(LOG_ERR, "Error from rtnetlink: %m"); 236 | } 237 | exit(1); 238 | } 239 | 240 | if (callback) { 241 | int err; 242 | 243 | if ((err = callback(hdr, arg)) == -1) { 244 | do_log(LOG_ERR, "Callback failed"); 245 | exit(1); 246 | } 247 | } 248 | 249 | skip_it: 250 | hdr = NLMSG_NEXT(hdr, status); 251 | } 252 | if (msg.msg_flags & MSG_TRUNC) { 253 | do_log(LOG_ERR, "Message truncated"); 254 | exit(1); 255 | } 256 | if (status) { 257 | do_log(LOG_ERR, "Dangling remnant of size %d!", status); 258 | exit(1); 259 | } 260 | } 261 | } 262 | 263 | 264 | int 265 | netlink_open(void) 266 | { 267 | int fd; 268 | 269 | if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { 270 | do_log(LOG_ERR, "Could not create netlink socket: %m"); 271 | exit(1); 272 | } 273 | 274 | close_on_exec(fd); 275 | 276 | struct sockaddr_nl addr; 277 | 278 | memset(&addr, 0, sizeof(addr)); 279 | addr.nl_family = AF_NETLINK; 280 | addr.nl_groups = RTMGRP_LINK; 281 | 282 | if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { 283 | do_log(LOG_ERR, "Could not bind netlink socket: %m"); 284 | exit(1); 285 | } 286 | 287 | socklen_t addr_len = sizeof(addr); 288 | 289 | if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) { 290 | do_log(LOG_ERR, "Could not get socket details: %m"); 291 | exit(1); 292 | } 293 | 294 | if (addr_len != sizeof(addr)) { 295 | do_log(LOG_ERR, "Our netlink socket size does not match the kernel's!"); 296 | exit(1); 297 | } 298 | 299 | if (addr.nl_family != AF_NETLINK) { 300 | do_log(LOG_ERR, "The kernel has given us an insane address family!"); 301 | exit(1); 302 | } 303 | 304 | return fd; 305 | } 306 | 307 | 308 | /* 309 | * Local variables: 310 | * c-file-style: "stroustrup" 311 | * End: 312 | */ 313 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c - daemon startup and link monitoring 3 | * 4 | * Copyright 2003 PathScale, Inc. 5 | * Copyright 2003, 2004, 2005 Bryan O'Sullivan 6 | * Copyright 2003 Jeremy Fitzhardinge 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License, 10 | * version 2, as published by the Free Software Foundation. You are 11 | * forbidden from redistributing or modifying it under the terms of 12 | * any other license, including other versions of the GNU General 13 | * Public License. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | */ 20 | 21 | #define _GNU_SOURCE 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "netplug.h" 38 | 39 | 40 | int use_syslog; 41 | static char *pid_file; 42 | 43 | static int 44 | handle_interface(struct nlmsghdr *hdr, void *arg) 45 | { 46 | if (hdr->nlmsg_type != RTM_NEWLINK && hdr->nlmsg_type != RTM_DELLINK) { 47 | return 0; 48 | } 49 | 50 | struct ifinfomsg *info = NLMSG_DATA(hdr); 51 | int len = hdr->nlmsg_len - NLMSG_LENGTH(sizeof(*info)); 52 | 53 | if (info->ifi_flags & IFF_LOOPBACK) { 54 | return 0; 55 | } 56 | 57 | if (len < 0) { 58 | do_log(LOG_ERR, "Malformed netlink packet length"); 59 | return -1; 60 | } 61 | 62 | struct rtattr *attrs[IFLA_MAX + 1]; 63 | 64 | parse_rtattrs(attrs, IFLA_MAX, IFLA_RTA(info), len); 65 | 66 | if (attrs[IFLA_IFNAME] == NULL) { 67 | do_log(LOG_ERR, "No interface name"); 68 | return -1; 69 | } 70 | 71 | char *name = RTA_DATA(attrs[IFLA_IFNAME]); 72 | 73 | if (!if_match(name)) { 74 | do_log(LOG_INFO, "%s: ignoring event", name); 75 | return 0; 76 | } 77 | 78 | struct if_info *i = if_info_get_interface(hdr, attrs); 79 | 80 | if (i == NULL) 81 | return 0; 82 | 83 | ifsm_flagchange(i, info->ifi_flags); 84 | 85 | if_info_update_interface(hdr, attrs); 86 | 87 | return 0; 88 | } 89 | 90 | 91 | static void 92 | usage(char *progname, int exitcode) 93 | { 94 | fprintf(stderr, "Usage: %s [-DFP] [-c config-file] [-s script-file] [-i interface] [-p pid-file]\n", 95 | progname); 96 | 97 | fprintf(stderr, "\t-D\t\t" 98 | "print extra debugging messages\n"); 99 | fprintf(stderr, "\t-F\t\t" 100 | "run in foreground (don't become a daemon)\n"); 101 | fprintf(stderr, "\t-P\t\t" 102 | "do not autoprobe for interfaces (use with care)\n"); 103 | fprintf(stderr, "\t-c config_file\t" 104 | "read interface patterns from this config file\n"); 105 | fprintf(stderr, "\t-s script_file\t" 106 | "script file for probing interfaces, bringing them up or down\n"); 107 | fprintf(stderr, "\t-i interface\t" 108 | "only handle interfaces matching this pattern\n"); 109 | fprintf(stderr, "\t-p pid_file\t" 110 | "write daemon process ID to pid_file\n"); 111 | 112 | exit(exitcode); 113 | } 114 | 115 | 116 | static void 117 | write_pid(void) 118 | { 119 | FILE *fp; 120 | 121 | if ((fp = fopen(pid_file, "w")) == NULL) { 122 | do_log(LOG_ERR, "%s: %m", pid_file); 123 | return; 124 | } 125 | 126 | fprintf(fp, "%d\n", getpid()); 127 | fclose(fp); 128 | } 129 | 130 | static void 131 | tidy_pid(void) 132 | { 133 | if (pid_file) { 134 | unlink(pid_file); 135 | pid_file = NULL; 136 | } 137 | } 138 | 139 | static void 140 | exit_handler(int sig) 141 | { 142 | tidy_pid(); 143 | do_log(LOG_ERR, "caught signal %d - exiting", sig); 144 | exit(1); 145 | } 146 | 147 | struct child_exit 148 | { 149 | pid_t pid; 150 | int status; 151 | }; 152 | 153 | static int child_handler_pipe[2]; 154 | 155 | static void 156 | child_handler(int sig, siginfo_t *info, void *v) 157 | { 158 | struct child_exit ce; 159 | int ret; 160 | ssize_t s = 0; 161 | 162 | assert(sig == SIGCHLD); 163 | 164 | ce.pid = info->si_pid; 165 | ret = waitpid(info->si_pid, &ce.status, 0); 166 | if (ret == info->si_pid) 167 | { 168 | s = write(child_handler_pipe[1], &ce, sizeof(ce)); 169 | 170 | if (s == -1) 171 | { 172 | do_log(LOG_ERR, "can't write into pipe"); 173 | exit(1); 174 | } 175 | } 176 | } 177 | 178 | /* Poll the existing interface state, so we can catch any state 179 | changes for which we may not have neen a netlink message. */ 180 | static void 181 | poll_interfaces(void) 182 | { 183 | static int sockfd = -1; 184 | 185 | if (sockfd == -1) { 186 | sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 187 | if (sockfd == -1) { 188 | do_log(LOG_ERR, "can't create interface socket: %m"); 189 | exit(1); 190 | } 191 | close_on_exec(sockfd); 192 | } 193 | 194 | int pollflags(struct if_info *info) { 195 | struct ifreq ifr; 196 | 197 | if (!if_match(info->name)) 198 | return 0; 199 | 200 | memcpy(ifr.ifr_name, info->name, sizeof(ifr.ifr_name)); 201 | if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) 202 | do_log(LOG_ERR, "%s: can't get flags: %m", info->name); 203 | else { 204 | ifsm_flagchange(info, ifr.ifr_flags); 205 | ifsm_flagpoll(info); 206 | } 207 | 208 | return 0; 209 | } 210 | 211 | for_each_iface(pollflags); 212 | } 213 | 214 | int debug = 0; 215 | 216 | int 217 | main(int argc, char *argv[]) 218 | { 219 | int foreground = 0; 220 | int cfg_read = 0; 221 | int probe = 1; 222 | int c; 223 | 224 | while ((c = getopt(argc, argv, "DFPc:s:hi:p:")) != EOF) { 225 | switch (c) { 226 | case 'D': 227 | debug = 1; 228 | break; 229 | case 'F': 230 | foreground = 1; 231 | break; 232 | case 'P': 233 | probe = 0; 234 | break; 235 | case 'c': 236 | read_config(optarg); 237 | cfg_read = 1; 238 | break; 239 | case 's': 240 | script_file = optarg; 241 | break; 242 | case 'h': 243 | fprintf(stderr, "netplugd version %s\n", NP_VERSION); 244 | usage(argv[0], 0); 245 | break; 246 | case 'i': 247 | if (save_pattern(optarg) == -1) { 248 | fprintf(stderr, "Bad pattern for '-i %s'\n", optarg); 249 | exit(1); 250 | } 251 | break; 252 | case 'p': 253 | pid_file = optarg; 254 | break; 255 | case '?': 256 | usage(argv[0], 1); 257 | } 258 | } 259 | 260 | if (!cfg_read) { 261 | read_config(NP_ETC_DIR "/netplugd.conf"); 262 | } 263 | 264 | if (getuid() != 0) { 265 | do_log(LOG_WARNING, "This daemon will not work properly unless " 266 | "run by root"); 267 | } 268 | 269 | if (probe) { 270 | probe_interfaces(); 271 | } 272 | 273 | struct sigaction act = { 274 | .sa_handler = exit_handler, 275 | .sa_flags = SA_ONESHOT | SA_NOMASK, 276 | }; 277 | 278 | if (sigaction(SIGHUP, &act, NULL) == -1) { 279 | do_log(LOG_ERR, "can't catch hangup signal: %m"); 280 | exit(1); 281 | } 282 | 283 | if (sigaction(SIGINT, &act, NULL) == -1) { 284 | do_log(LOG_ERR, "can't catch interrupt signal: %m"); 285 | exit(1); 286 | } 287 | 288 | if (sigaction(SIGTERM, &act, NULL) == -1) { 289 | do_log(LOG_ERR, "can't catch termination signal: %m"); 290 | exit(1); 291 | } 292 | 293 | if (!foreground) { 294 | use_syslog = 1; 295 | openlog("netplugd", LOG_PID, LOG_DAEMON); 296 | } 297 | 298 | if (pipe(child_handler_pipe) == -1) { 299 | do_log(LOG_ERR, "can't create pipe: %m"); 300 | exit(1); 301 | } 302 | 303 | close_on_exec(child_handler_pipe[0]); 304 | close_on_exec(child_handler_pipe[1]); 305 | 306 | if (fcntl(child_handler_pipe[0], F_SETFL, O_NONBLOCK) == -1) { 307 | do_log(LOG_ERR, "can't set pipe non-blocking: %m"); 308 | exit(1); 309 | } 310 | 311 | struct sigaction sa; 312 | sa.sa_sigaction = child_handler; 313 | sa.sa_flags = SA_RESTART | SA_SIGINFO; 314 | sigfillset(&sa.sa_mask); 315 | 316 | if (sigaction(SIGCHLD, &sa, NULL) == -1) { 317 | do_log(LOG_ERR, "can't set SIGCHLD handler: %m"); 318 | exit(1); 319 | } 320 | 321 | int fd = netlink_open(); 322 | 323 | netlink_request_dump(fd); 324 | netlink_receive_dump(fd, if_info_save_interface, NULL); 325 | 326 | if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { 327 | do_log(LOG_ERR, "can't set socket non-blocking: %m"); 328 | exit(1); 329 | } 330 | 331 | if (!foreground) { 332 | if (daemon(0, 0) == -1) { 333 | do_log(LOG_ERR, "daemon: %m"); 334 | exit(1); 335 | } 336 | 337 | if (pid_file) { 338 | atexit(tidy_pid); 339 | write_pid(); 340 | } 341 | } 342 | 343 | struct pollfd fds[] = { 344 | { fd, POLLIN, 0 }, 345 | { child_handler_pipe[0], POLLIN, 0 }, 346 | }; 347 | 348 | { 349 | /* Run over each of the interfaces we know and care about, and 350 | make sure the state machine has done the appropriate thing 351 | for their current state. */ 352 | int poll_flags(struct if_info *i) { 353 | if (if_match(i->name)) 354 | ifsm_flagpoll(i); 355 | return 0; 356 | } 357 | for_each_iface(poll_flags); 358 | } 359 | 360 | for(;;) { 361 | int ret; 362 | 363 | /* Make sure we don't miss anything interesting */ 364 | poll_interfaces(); 365 | 366 | ret = poll(fds, sizeof(fds)/sizeof(fds[0]), -1); 367 | 368 | if (ret == -1) { 369 | if (errno == EINTR) 370 | continue; 371 | do_log(LOG_ERR, "poll failed: %m"); 372 | exit(1); 373 | } 374 | if (ret == 0) { /* XXX??? */ 375 | sleep(1); /* don't spin */ 376 | continue; 377 | } 378 | 379 | if (fds[0].revents & POLLIN) { 380 | /* interface flag state change */ 381 | if (netlink_listen(fd, handle_interface, NULL) == 0) 382 | break; /* done */ 383 | } 384 | 385 | if (fds[1].revents & POLLIN) { 386 | /* netplug script finished */ 387 | int ret; 388 | struct child_exit ce; 389 | 390 | do { 391 | ret = read(child_handler_pipe[0], &ce, sizeof(ce)); 392 | 393 | assert(ret == 0 || ret == -1 || ret == sizeof(ce)); 394 | 395 | if (ret == sizeof(ce)) 396 | ifsm_scriptdone(ce.pid, ce.status); 397 | else if (ret == -1 && errno != EAGAIN) { 398 | do_log(LOG_ERR, "pipe read failed: %m"); 399 | exit(1); 400 | } 401 | } while(ret == sizeof(ce)); 402 | } 403 | } 404 | 405 | return 0; 406 | } 407 | 408 | 409 | /* 410 | * Local variables: 411 | * c-file-style: "stroustrup" 412 | * End: 413 | */ 414 | -------------------------------------------------------------------------------- /if_info.c: -------------------------------------------------------------------------------- 1 | /* 2 | * if_info.c - track network interface information 3 | * 4 | * Copyright 2003 PathScale, Inc. 5 | * Copyright 2003, 2004, 2005 Bryan O'Sullivan 6 | * Copyright 2003 Jeremy Fitzhardinge 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License, 10 | * version 2, as published by the Free Software Foundation. You are 11 | * forbidden from redistributing or modifying it under the terms of 12 | * any other license, including other versions of the GNU General 13 | * Public License. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "netplug.h" 31 | 32 | #define INFOHASHSZ 16 /* must be a power of 2 */ 33 | static struct if_info *if_info[INFOHASHSZ]; 34 | 35 | static const char * 36 | statename(enum ifstate s) 37 | { 38 | switch(s) { 39 | #define S(x) case ST_##x: return #x 40 | S(DOWN); 41 | S(DOWNANDOUT); 42 | S(PROBING); 43 | S(PROBING_UP); 44 | S(INACTIVE); 45 | S(INNING); 46 | S(WAIT_IN); 47 | S(ACTIVE); 48 | S(OUTING); 49 | S(INSANE); 50 | #undef S 51 | default: return "???"; 52 | } 53 | } 54 | 55 | static const char * 56 | flags_str(char *buf, unsigned int fl) 57 | { 58 | static struct flag { 59 | const char *name; 60 | unsigned int flag; 61 | } flags[] = { 62 | #define F(x) { #x, IFF_##x } 63 | F(UP), 64 | F(BROADCAST), 65 | F(DEBUG), 66 | F(LOOPBACK), 67 | F(POINTOPOINT), 68 | F(NOTRAILERS), 69 | F(RUNNING), 70 | F(NOARP), 71 | F(PROMISC), 72 | F(ALLMULTI), 73 | F(MASTER), 74 | F(SLAVE), 75 | F(MULTICAST), 76 | #undef F 77 | }; 78 | char *cp = buf; 79 | 80 | *cp = '\0'; 81 | 82 | for(int i = 0; i < sizeof(flags)/sizeof(*flags); i++) { 83 | if (fl & flags[i].flag) { 84 | fl &= ~flags[i].flag; 85 | cp += sprintf(cp, "%s,", flags[i].name); 86 | } 87 | } 88 | 89 | if (fl != 0) 90 | cp += sprintf(cp, "%x,", fl); 91 | 92 | if (cp != buf) 93 | cp[-1] = '\0'; 94 | 95 | return buf; 96 | } 97 | 98 | void 99 | for_each_iface(int (*func)(struct if_info *)) 100 | { 101 | for(int i = 0; i < INFOHASHSZ; i++) { 102 | for(struct if_info *info = if_info[i]; info != NULL; info = info->next) { 103 | if ((*func)(info)) 104 | return; 105 | } 106 | } 107 | } 108 | 109 | /* Reevaluate the state machine based on the current state and flag settings */ 110 | void 111 | ifsm_flagpoll(struct if_info *info) 112 | { 113 | enum ifstate state = info->state; 114 | 115 | switch(info->state) { 116 | case ST_DOWN: 117 | if ((info->flags & (IFF_UP|IFF_RUNNING)) == 0) 118 | break; 119 | /* FALLTHROUGH */ 120 | case ST_INACTIVE: 121 | if (!(info->flags & IFF_UP)) { 122 | assert(info->worker == -1); 123 | info->worker = run_netplug_bg(info->name, "probe"); 124 | info->state = ST_PROBING; 125 | } else if (info->flags & IFF_RUNNING) { 126 | assert(info->worker == -1); 127 | info->worker = run_netplug_bg(info->name, "in"); 128 | info->state = ST_INNING; 129 | } 130 | break; 131 | 132 | case ST_PROBING: 133 | case ST_PROBING_UP: 134 | case ST_WAIT_IN: 135 | case ST_DOWNANDOUT: 136 | break; 137 | 138 | case ST_INNING: 139 | if (!(info->flags & IFF_RUNNING)) 140 | info->state = ST_WAIT_IN; 141 | break; 142 | 143 | case ST_ACTIVE: 144 | if (!(info->flags & IFF_RUNNING)) { 145 | assert(info->worker == -1); 146 | info->worker = run_netplug_bg(info->name, "out"); 147 | info->state = ST_OUTING; 148 | } 149 | break; 150 | 151 | case ST_OUTING: 152 | if (!(info->flags & IFF_UP)) 153 | info->state = ST_DOWNANDOUT; 154 | break; 155 | 156 | case ST_INSANE: 157 | break; 158 | } 159 | 160 | if (info->state != state) 161 | do_log(LOG_DEBUG, "ifsm_flagpoll %s: moved from state %s to %s", 162 | info->name, statename(state), statename(info->state)); 163 | } 164 | 165 | /* if_info state machine transitions caused by interface flag changes (edge triggered) */ 166 | void 167 | ifsm_flagchange(struct if_info *info, unsigned int newflags) 168 | { 169 | unsigned int changed = (info->flags ^ newflags) & (IFF_RUNNING | IFF_UP); 170 | 171 | if (changed == 0) 172 | return; 173 | 174 | char buf1[512], buf2[512]; 175 | do_log(LOG_INFO, "%s: state %s flags 0x%08x %s -> 0x%08x %s", info->name, 176 | statename(info->state), 177 | info->flags, flags_str(buf1, info->flags), 178 | newflags, flags_str(buf2, newflags)); 179 | 180 | /* XXX put interface state-change rate limiting here */ 181 | if (0 /* flapping */) { 182 | info->state = ST_INSANE; 183 | } 184 | 185 | if (changed & IFF_UP) { 186 | if (newflags & IFF_UP) { 187 | switch(info->state) { 188 | case ST_DOWN: 189 | info->state = ST_INACTIVE; 190 | break; 191 | 192 | case ST_PROBING: 193 | info->state = ST_PROBING_UP; 194 | break; 195 | 196 | default: 197 | do_log(LOG_ERR, "%s: unexpected state %s for UP", info->name, statename(info->state)); 198 | exit(1); 199 | } 200 | } else { 201 | /* interface went down */ 202 | switch(info->state) { 203 | case ST_OUTING: 204 | /* went down during an OUT script - OK */ 205 | info->state = ST_DOWNANDOUT; 206 | break; 207 | 208 | case ST_DOWN: 209 | /* already down */ 210 | break; 211 | 212 | case ST_PROBING: 213 | /* already probing - don't do anything rash */ 214 | break; 215 | 216 | case ST_PROBING_UP: 217 | /* well, we were up, but now we're not */ 218 | info->state = ST_PROBING; 219 | break; 220 | 221 | default: 222 | /* All other states: kill off any scripts currently 223 | running, and go into the PROBING state, attempting 224 | to bring it up */ 225 | kill_script(info->worker); 226 | info->state = ST_PROBING; 227 | info->worker = run_netplug_bg(info->name, "probe"); 228 | } 229 | } 230 | } 231 | 232 | if (changed & IFF_RUNNING) { 233 | switch(info->state) { 234 | case ST_INACTIVE: 235 | assert(!(info->flags & IFF_RUNNING)); 236 | assert(info->worker == -1); 237 | 238 | info->worker = run_netplug_bg(info->name, "in"); 239 | info->state = ST_INNING; 240 | break; 241 | 242 | case ST_INNING: 243 | assert(info->flags & IFF_RUNNING); 244 | info->state = ST_WAIT_IN; 245 | break; 246 | 247 | case ST_WAIT_IN: 248 | /* unaffected by interface flag changing */ 249 | break; 250 | 251 | case ST_ACTIVE: 252 | assert(info->flags & IFF_RUNNING); 253 | assert(info->worker == -1); 254 | 255 | info->worker = run_netplug_bg(info->name, "out"); 256 | info->state = ST_OUTING; 257 | break; 258 | 259 | case ST_OUTING: 260 | /* always go to INACTIVE regardless of flag state */ 261 | break; 262 | 263 | case ST_PROBING: 264 | case ST_PROBING_UP: 265 | /* ignore running state */ 266 | break; 267 | 268 | case ST_INSANE: 269 | /* stay insane until there's been quiet for a while, then 270 | down interface and switch to ST_DOWN */ 271 | break; 272 | 273 | case ST_DOWN: 274 | case ST_DOWNANDOUT: 275 | /* badness: somehow interface became RUNNING without being 276 | UP - ignore it */ 277 | break; 278 | } 279 | } 280 | 281 | do_log(LOG_DEBUG, "%s: moved to state %s; worker %d", 282 | info->name, statename(info->state), info->worker); 283 | info->flags = newflags; 284 | info->lastchange = time(0); 285 | } 286 | 287 | /* handle a script termination and update the state accordingly */ 288 | void ifsm_scriptdone(pid_t pid, int exitstatus) 289 | { 290 | int exitok = WIFEXITED(exitstatus) && WEXITSTATUS(exitstatus) == 0; 291 | struct if_info *info; 292 | assert(WIFEXITED(exitstatus) || WIFSIGNALED(exitstatus)); 293 | 294 | int find_pid(struct if_info *i) { 295 | if (i->worker == pid) { 296 | info = i; 297 | return 1; 298 | } 299 | return 0; 300 | } 301 | 302 | info = NULL; 303 | for_each_iface(find_pid); 304 | 305 | if (info == NULL) { 306 | do_log(LOG_INFO, "Unexpected child %d exited with status %d", 307 | pid, exitstatus); 308 | return; 309 | } 310 | 311 | do_log(LOG_INFO, "%s: state %s pid %d exited status %d", 312 | info->name, statename(info->state), pid, exitstatus); 313 | 314 | info->worker = -1; 315 | 316 | switch(info->state) { 317 | case ST_PROBING: 318 | /* If we're still in PROBING state, then it means that the 319 | interface flags have not come up, even though the script 320 | finished. Go back to DOWN and wait for the UP flag 321 | setting. */ 322 | if (!exitok) 323 | do_log(LOG_WARNING, "Could not bring %s back up", info->name); 324 | 325 | info->state = ST_DOWN; 326 | break; 327 | 328 | case ST_PROBING_UP: 329 | /* regardless of script's exit status, the interface is 330 | actually up now, so just make it inactive */ 331 | info->state = ST_INACTIVE; 332 | break; 333 | 334 | case ST_DOWNANDOUT: 335 | /* we were just waiting for the out script to finish - start a 336 | probe script for this interface */ 337 | info->state = ST_PROBING; 338 | assert(info->worker == -1); 339 | info->worker = run_netplug_bg(info->name, "probe"); 340 | break; 341 | 342 | case ST_INNING: 343 | if (exitok) 344 | info->state = ST_ACTIVE; 345 | else 346 | info->state = ST_INSANE; /* ??? */ 347 | break; 348 | 349 | case ST_OUTING: 350 | /* What if !exitok? What if interface is still active? ->ST_INSANE? */ 351 | info->state = ST_INACTIVE; 352 | break; 353 | 354 | case ST_WAIT_IN: 355 | assert(info->worker == -1); 356 | 357 | info->worker = run_netplug_bg(info->name, "out"); 358 | info->state = ST_OUTING; 359 | break; 360 | 361 | case ST_INACTIVE: 362 | case ST_ACTIVE: 363 | case ST_INSANE: 364 | case ST_DOWN: 365 | do_log(LOG_ERR, "ifsm_scriptdone: %s: bad state %s for script termination", 366 | info->name, statename(info->state)); 367 | exit(1); 368 | } 369 | 370 | do_log(LOG_DEBUG, "%s: moved to state %s", info->name, statename(info->state)); 371 | } 372 | 373 | void 374 | parse_rtattrs(struct rtattr *tb[], int max, struct rtattr *rta, int len) 375 | { 376 | memset(tb, 0, sizeof(tb) * (max + 1)); 377 | 378 | while (RTA_OK(rta, len)) { 379 | if (rta->rta_type <= max) 380 | tb[rta->rta_type] = rta; 381 | rta = RTA_NEXT(rta,len); 382 | } 383 | if (len) { 384 | do_log(LOG_ERR, "Badness! Deficit %d, rta_len=%d", len, rta->rta_len); 385 | abort(); 386 | } 387 | } 388 | 389 | int if_info_save_interface(struct nlmsghdr *hdr, void *arg) 390 | { 391 | struct rtattr *attrs[IFLA_MAX + 1]; 392 | struct ifinfomsg *info = NLMSG_DATA(hdr); 393 | 394 | parse_rtattrs(attrs, IFLA_MAX, IFLA_RTA(info), IFLA_PAYLOAD(hdr)); 395 | 396 | return if_info_update_interface(hdr, attrs) ? 0 : -1; 397 | } 398 | 399 | 400 | struct if_info * 401 | if_info_get_interface(struct nlmsghdr *hdr, struct rtattr *attrs[]) 402 | { 403 | if (hdr->nlmsg_type != RTM_NEWLINK) { 404 | return NULL; 405 | } 406 | 407 | struct ifinfomsg *info = NLMSG_DATA(hdr); 408 | 409 | if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(info))) { 410 | return NULL; 411 | } 412 | 413 | if (attrs[IFLA_IFNAME] == NULL) { 414 | return NULL; 415 | } 416 | 417 | int x = info->ifi_index & (INFOHASHSZ-1); 418 | struct if_info *i, **ip; 419 | 420 | for (ip = &if_info[x]; (i = *ip) != NULL; ip = &i->next) { 421 | if (i->index == info->ifi_index) { 422 | break; 423 | } 424 | } 425 | 426 | if (i == NULL) { 427 | i = xmalloc(sizeof(*i)); 428 | i->next = *ip; 429 | i->index = info->ifi_index; 430 | *ip = i; 431 | 432 | /* initialize state machine fields */ 433 | i->state = ST_DOWN; 434 | i->lastchange = 0; 435 | i->worker = -1; 436 | } 437 | return i; 438 | } 439 | 440 | 441 | struct if_info * 442 | if_info_update_interface(struct nlmsghdr *hdr, struct rtattr *attrs[]) 443 | { 444 | struct ifinfomsg *info = NLMSG_DATA(hdr); 445 | struct if_info *i; 446 | 447 | if ((i = if_info_get_interface(hdr, attrs)) == NULL) { 448 | return NULL; 449 | } 450 | 451 | i->type = info->ifi_type; 452 | i->flags = info->ifi_flags; 453 | 454 | if (attrs[IFLA_ADDRESS]) { 455 | int alen; 456 | i->addr_len = alen = RTA_PAYLOAD(attrs[IFLA_ADDRESS]); 457 | if (alen > sizeof(i->addr)) 458 | alen = sizeof(i->addr); 459 | memcpy(i->addr, RTA_DATA(attrs[IFLA_ADDRESS]), alen); 460 | } else { 461 | i->addr_len = 0; 462 | memset(i->addr, 0, sizeof(i->addr)); 463 | } 464 | 465 | strcpy(i->name, RTA_DATA(attrs[IFLA_IFNAME])); 466 | 467 | return i; 468 | } 469 | 470 | 471 | /* 472 | * Local variables: 473 | * c-file-style: "stroustrup" 474 | * End: 475 | */ 476 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place - Suite 330 6 | Boston, MA 02111-1307, USA. 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your 13 | freedom to share and change it. By contrast, the GNU General Public 14 | License is intended to guarantee your freedom to share and change free 15 | software--to make sure the software is free for all its users. This 16 | General Public License applies to most of the Free Software 17 | Foundation's software and to any other program whose authors commit to 18 | using it. (Some other Free Software Foundation software is covered by 19 | the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | this service if you wish), that you receive source code or can get it 26 | if you want it, that you can change the software or use pieces of it 27 | in new free programs; and that you know you can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid 30 | anyone to deny you these rights or to ask you to surrender the rights. 31 | These restrictions translate to certain responsibilities for you if 32 | you distribute copies of the software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must give the recipients all the rights that 36 | you have. You must make sure that they, too, receive or can get the 37 | source code. And you must show them these terms so they know their 38 | rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, 41 | and (2) offer you this license which gives you legal permission to 42 | copy, distribute and/or modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain 45 | that everyone understands that there is no warranty for this free 46 | software. If the software is modified by someone else and passed on, 47 | we want its recipients to know that what they have is not the 48 | original, so that any problems introduced by others will not reflect 49 | on the original authors' reputations. 50 | 51 | Finally, any free program is threatened constantly by software 52 | patents. We wish to avoid the danger that redistributors of a free 53 | program will individually obtain patent licenses, in effect making the 54 | program proprietary. To prevent this, we have made it clear that any 55 | patent must be licensed for everyone's free use or not licensed at 56 | all. 57 | 58 | The precise terms and conditions for copying, distribution and 59 | modification follow. 60 | 61 | GNU GENERAL PUBLIC LICENSE 62 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 63 | 64 | 0. This License applies to any program or other work which contains 65 | a notice placed by the copyright holder saying it may be distributed 66 | under the terms of this General Public License. The "Program", below, 67 | refers to any such program or work, and a "work based on the Program" 68 | means either the Program or any derivative work under copyright law: 69 | that is to say, a work containing the Program or a portion of it, 70 | either verbatim or with modifications and/or translated into another 71 | language. (Hereinafter, translation is included without limitation in 72 | the term "modification".) Each licensee is addressed as "you". 73 | 74 | Activities other than copying, distribution and modification are not 75 | covered by this License; they are outside its scope. The act of 76 | running the Program is not restricted, and the output from the Program 77 | is covered only if its contents constitute a work based on the Program 78 | (independent of having been made by running the Program). Whether 79 | that is true depends on what the Program does. 80 | 81 | 1. You may copy and distribute verbatim copies of the Program's 82 | source code as you receive it, in any medium, provided that you 83 | conspicuously and appropriately publish on each copy an appropriate 84 | copyright notice and disclaimer of warranty; keep intact all the 85 | notices that refer to this License and to the absence of any warranty; 86 | and give any other recipients of the Program a copy of this License 87 | along with the Program. 88 | 89 | You may charge a fee for the physical act of transferring a copy, and 90 | you may at your option offer warranty protection in exchange for a 91 | fee. 92 | 93 | 2. You may modify your copy or copies of the Program or any portion 94 | of it, thus forming a work based on the Program, and copy and 95 | distribute such modifications or work under the terms of Section 1 96 | above, provided that you also meet all of these conditions: 97 | 98 | a) You must cause the modified files to carry prominent notices 99 | stating that you changed the files and the date of any change. 100 | 101 | b) You must cause any work that you distribute or publish, that in 102 | whole or in part contains or is derived from the Program or any 103 | part thereof, to be licensed as a whole at no charge to all third 104 | parties under the terms of this License. 105 | 106 | c) If the modified program normally reads commands interactively 107 | when run, you must cause it, when started running for such 108 | interactive use in the most ordinary way, to print or display an 109 | announcement including an appropriate copyright notice and a 110 | notice that there is no warranty (or else, saying that you provide 111 | a warranty) and that users may redistribute the program under 112 | these conditions, and telling the user how to view a copy of this 113 | License. (Exception: if the Program itself is interactive but 114 | does not normally print such an announcement, your work based on 115 | the Program is not required to print an announcement.) 116 | 117 | These requirements apply to the modified work as a whole. If 118 | identifiable sections of that work are not derived from the Program, 119 | and can be reasonably considered independent and separate works in 120 | themselves, then this License, and its terms, do not apply to those 121 | sections when you distribute them as separate works. But when you 122 | distribute the same sections as part of a whole which is a work based 123 | on the Program, the distribution of the whole must be on the terms of 124 | this License, whose permissions for other licensees extend to the 125 | entire whole, and thus to each and every part regardless of who wrote 126 | it. 127 | 128 | Thus, it is not the intent of this section to claim rights or contest 129 | your rights to work written entirely by you; rather, the intent is to 130 | exercise the right to control the distribution of derivative or 131 | collective works based on the Program. 132 | 133 | In addition, mere aggregation of another work not based on the Program 134 | with the Program (or with a work based on the Program) on a volume of 135 | a storage or distribution medium does not bring the other work under 136 | the scope of this License. 137 | 138 | 3. You may copy and distribute the Program (or a work based on it, 139 | under Section 2) in object code or executable form under the terms of 140 | Sections 1 and 2 above provided that you also do one of the following: 141 | 142 | a) Accompany it with the complete corresponding machine-readable 143 | source code, which must be distributed under the terms of Sections 144 | 1 and 2 above on a medium customarily used for software 145 | interchange; or, 146 | 147 | b) Accompany it with a written offer, valid for at least three 148 | years, to give any third party, for a charge no more than your 149 | cost of physically performing source distribution, a complete 150 | machine-readable copy of the corresponding source code, to be 151 | distributed under the terms of Sections 1 and 2 above on a medium 152 | customarily used for software interchange; or, 153 | 154 | c) Accompany it with the information you received as to the offer 155 | to distribute corresponding source code. (This alternative is 156 | allowed only for noncommercial distribution and only if you 157 | received the program in object code or executable form with such 158 | an offer, in accord with Subsection b above.) 159 | 160 | The source code for a work means the preferred form of the work for 161 | making modifications to it. For an executable work, complete source 162 | code means all the source code for all modules it contains, plus any 163 | associated interface definition files, plus the scripts used to 164 | control compilation and installation of the executable. However, as a 165 | special exception, the source code distributed need not include 166 | anything that is normally distributed (in either source or binary 167 | form) with the major components (compiler, kernel, and so on) of the 168 | operating system on which the executable runs, unless that component 169 | itself accompanies the executable. 170 | 171 | If distribution of executable or object code is made by offering 172 | access to copy from a designated place, then offering equivalent 173 | access to copy the source code from the same place counts as 174 | distribution of the source code, even though third parties are not 175 | compelled to copy the source along with the object code. 176 | 177 | 4. You may not copy, modify, sublicense, or distribute the Program 178 | except as expressly provided under this License. Any attempt 179 | otherwise to copy, modify, sublicense or distribute the Program is 180 | void, and will automatically terminate your rights under this License. 181 | However, parties who have received copies, or rights, from you under 182 | this License will not have their licenses terminated so long as such 183 | parties remain in full compliance. 184 | 185 | 5. You are not required to accept this License, since you have not 186 | signed it. However, nothing else grants you permission to modify or 187 | distribute the Program or its derivative works. These actions are 188 | prohibited by law if you do not accept this License. Therefore, by 189 | modifying or distributing the Program (or any work based on the 190 | Program), you indicate your acceptance of this License to do so, and 191 | all its terms and conditions for copying, distributing or modifying 192 | the Program or works based on it. 193 | 194 | 6. Each time you redistribute the Program (or any work based on the 195 | Program), the recipient automatically receives a license from the 196 | original licensor to copy, distribute or modify the Program subject to 197 | these terms and conditions. You may not impose any further 198 | restrictions on the recipients' exercise of the rights granted herein. 199 | You are not responsible for enforcing compliance by third parties to 200 | this License. 201 | 202 | 7. If, as a consequence of a court judgment or allegation of patent 203 | infringement or for any other reason (not limited to patent issues), 204 | conditions are imposed on you (whether by court order, agreement or 205 | otherwise) that contradict the conditions of this License, they do not 206 | excuse you from the conditions of this License. If you cannot 207 | distribute so as to satisfy simultaneously your obligations under this 208 | License and any other pertinent obligations, then as a consequence you 209 | may not distribute the Program at all. For example, if a patent 210 | license would not permit royalty-free redistribution of the Program by 211 | all those who receive copies directly or indirectly through you, then 212 | the only way you could satisfy both it and this License would be to 213 | refrain entirely from distribution of the Program. 214 | 215 | If any portion of this section is held invalid or unenforceable under 216 | any particular circumstance, the balance of the section is intended to 217 | apply and the section as a whole is intended to apply in other 218 | circumstances. 219 | 220 | It is not the purpose of this section to induce you to infringe any 221 | patents or other property right claims or to contest validity of any 222 | such claims; this section has the sole purpose of protecting the 223 | integrity of the free software distribution system, which is 224 | implemented by public license practices. Many people have made 225 | generous contributions to the wide range of software distributed 226 | through that system in reliance on consistent application of that 227 | system; it is up to the author/donor to decide if he or she is willing 228 | to distribute software through any other system and a licensee cannot 229 | impose that choice. 230 | 231 | This section is intended to make thoroughly clear what is believed to 232 | be a consequence of the rest of this License. 233 | 234 | 8. If the distribution and/or use of the Program is restricted in 235 | certain countries either by patents or by copyrighted interfaces, the 236 | original copyright holder who places the Program under this License 237 | may add an explicit geographical distribution limitation excluding 238 | those countries, so that distribution is permitted only in or among 239 | countries not thus excluded. In such case, this License incorporates 240 | the limitation as if written in the body of this License. 241 | 242 | 9. The Free Software Foundation may publish revised and/or new 243 | versions of the General Public License from time to time. Such new 244 | versions will be similar in spirit to the present version, but may 245 | differ in detail to address new problems or concerns. 246 | 247 | Each version is given a distinguishing version number. If the Program 248 | specifies a version number of this License which applies to it and 249 | "any later version", you have the option of following the terms and 250 | conditions either of that version or of any later version published by 251 | the Free Software Foundation. If the Program does not specify a 252 | version number of this License, you may choose any version ever 253 | published by the Free Software Foundation. 254 | 255 | 10. If you wish to incorporate parts of the Program into other free 256 | programs whose distribution conditions are different, write to the 257 | author to ask for permission. For software which is copyrighted by 258 | the Free Software Foundation, write to the Free Software Foundation; 259 | we sometimes make exceptions for this. Our decision will be guided by 260 | the two goals of preserving the free status of all derivatives of our 261 | free software and of promoting the sharing and reuse of software 262 | generally. 263 | 264 | NO WARRANTY 265 | 266 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO 267 | WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 268 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 269 | OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY 270 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 271 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 272 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 273 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 274 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 275 | 276 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 277 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 278 | AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU 279 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 280 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 281 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 282 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 283 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF 284 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 285 | DAMAGES. 286 | 287 | END OF TERMS AND CONDITIONS 288 | 289 | How to Apply These Terms to Your New Programs 290 | 291 | If you develop a new program, and you want it to be of the greatest 292 | possible use to the public, the best way to achieve this is to make it 293 | free software which everyone can redistribute and change under these 294 | terms. 295 | 296 | To do so, attach the following notices to the program. It is safest 297 | to attach them to the start of each source file to most effectively 298 | convey the exclusion of warranty; and each file should have at least 299 | the "copyright" line and a pointer to where the full notice is found. 300 | 301 | 302 | Copyright (C) 19yy 303 | 304 | This program is free software; you can redistribute it and/or 305 | modify it under the terms of the GNU General Public License as 306 | published by the Free Software Foundation; either version 2 of the 307 | License, or (at your option) any later version. 308 | 309 | This program is distributed in the hope that it will be useful, 310 | but WITHOUT ANY WARRANTY; without even the implied warranty of 311 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 312 | General Public License for more details. 313 | 314 | You should have received a copy of the GNU General Public License 315 | along with this program; see the file COPYING. If not, write to 316 | the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 317 | Boston, MA 02111-1307, USA. 318 | 319 | Also add information on how to contact you by electronic and paper mail. 320 | 321 | If the program is interactive, make it output a short notice like this 322 | when it starts in an interactive mode: 323 | 324 | Gnomovision version 69, Copyright (C) 19yy name of author 325 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 326 | This is free software, and you are welcome to redistribute it 327 | under certain conditions; type `show c' for details. 328 | 329 | The hypothetical commands `show w' and `show c' should show the 330 | appropriate parts of the General Public License. Of course, the 331 | commands you use may be called something other than `show w' and `show 332 | c'; they could even be mouse-clicks or menu items--whatever suits your 333 | program. 334 | 335 | You should also get your employer (if you work as a programmer) or 336 | your school, if any, to sign a "copyright disclaimer" for the program, 337 | if necessary. Here is a sample; alter the names: 338 | 339 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 340 | program `Gnomovision' (which makes passes at compilers) written by 341 | James Hacker. 342 | 343 | , 1 April 1989 344 | Ty Coon, President of Vice 345 | 346 | This General Public License does not permit incorporating your program 347 | into proprietary programs. If your program is a subroutine library, 348 | you may consider it more useful to permit linking proprietary 349 | applications with the library. If this is what you want to do, use 350 | the GNU Library General Public License instead of this License. 351 | --------------------------------------------------------------------------------