diff options
-rw-r--r-- | include/linux/if.h | 26 | ||||
-rw-r--r-- | include/linux/netdevice.h | 35 | ||||
-rw-r--r-- | include/linux/rtnetlink.h | 2 | ||||
-rw-r--r-- | net/core/dev.c | 14 | ||||
-rw-r--r-- | net/core/link_watch.c | 40 | ||||
-rw-r--r-- | net/core/net-sysfs.c | 41 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 50 |
7 files changed, 200 insertions, 8 deletions
diff --git a/include/linux/if.h b/include/linux/if.h index 12c6f6d157c3..374e20ad8b0d 100644 --- a/include/linux/if.h +++ b/include/linux/if.h | |||
@@ -33,7 +33,7 @@ | |||
33 | #define IFF_LOOPBACK 0x8 /* is a loopback net */ | 33 | #define IFF_LOOPBACK 0x8 /* is a loopback net */ |
34 | #define IFF_POINTOPOINT 0x10 /* interface is has p-p link */ | 34 | #define IFF_POINTOPOINT 0x10 /* interface is has p-p link */ |
35 | #define IFF_NOTRAILERS 0x20 /* avoid use of trailers */ | 35 | #define IFF_NOTRAILERS 0x20 /* avoid use of trailers */ |
36 | #define IFF_RUNNING 0x40 /* interface running and carrier ok */ | 36 | #define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */ |
37 | #define IFF_NOARP 0x80 /* no ARP protocol */ | 37 | #define IFF_NOARP 0x80 /* no ARP protocol */ |
38 | #define IFF_PROMISC 0x100 /* receive all packets */ | 38 | #define IFF_PROMISC 0x100 /* receive all packets */ |
39 | #define IFF_ALLMULTI 0x200 /* receive all multicast packets*/ | 39 | #define IFF_ALLMULTI 0x200 /* receive all multicast packets*/ |
@@ -43,12 +43,16 @@ | |||
43 | 43 | ||
44 | #define IFF_MULTICAST 0x1000 /* Supports multicast */ | 44 | #define IFF_MULTICAST 0x1000 /* Supports multicast */ |
45 | 45 | ||
46 | #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MASTER|IFF_SLAVE|IFF_RUNNING) | ||
47 | |||
48 | #define IFF_PORTSEL 0x2000 /* can set media type */ | 46 | #define IFF_PORTSEL 0x2000 /* can set media type */ |
49 | #define IFF_AUTOMEDIA 0x4000 /* auto media select active */ | 47 | #define IFF_AUTOMEDIA 0x4000 /* auto media select active */ |
50 | #define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/ | 48 | #define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/ |
51 | 49 | ||
50 | #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ | ||
51 | #define IFF_DORMANT 0x20000 /* driver signals dormant */ | ||
52 | |||
53 | #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|\ | ||
54 | IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) | ||
55 | |||
52 | /* Private (from user) interface flags (netdevice->priv_flags). */ | 56 | /* Private (from user) interface flags (netdevice->priv_flags). */ |
53 | #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ | 57 | #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ |
54 | #define IFF_EBRIDGE 0x2 /* Ethernet bridging device. */ | 58 | #define IFF_EBRIDGE 0x2 /* Ethernet bridging device. */ |
@@ -83,6 +87,22 @@ | |||
83 | #define IF_PROTO_FR_ETH_PVC 0x200B | 87 | #define IF_PROTO_FR_ETH_PVC 0x200B |
84 | #define IF_PROTO_RAW 0x200C /* RAW Socket */ | 88 | #define IF_PROTO_RAW 0x200C /* RAW Socket */ |
85 | 89 | ||
90 | /* RFC 2863 operational status */ | ||
91 | enum { | ||
92 | IF_OPER_UNKNOWN, | ||
93 | IF_OPER_NOTPRESENT, | ||
94 | IF_OPER_DOWN, | ||
95 | IF_OPER_LOWERLAYERDOWN, | ||
96 | IF_OPER_TESTING, | ||
97 | IF_OPER_DORMANT, | ||
98 | IF_OPER_UP, | ||
99 | }; | ||
100 | |||
101 | /* link modes */ | ||
102 | enum { | ||
103 | IF_LINK_MODE_DEFAULT, | ||
104 | IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ | ||
105 | }; | ||
86 | 106 | ||
87 | /* | 107 | /* |
88 | * Device mapping structure. I'd just gone off and designed a | 108 | * Device mapping structure. I'd just gone off and designed a |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7fda03d338d1..b825be201bce 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -230,7 +230,8 @@ enum netdev_state_t | |||
230 | __LINK_STATE_SCHED, | 230 | __LINK_STATE_SCHED, |
231 | __LINK_STATE_NOCARRIER, | 231 | __LINK_STATE_NOCARRIER, |
232 | __LINK_STATE_RX_SCHED, | 232 | __LINK_STATE_RX_SCHED, |
233 | __LINK_STATE_LINKWATCH_PENDING | 233 | __LINK_STATE_LINKWATCH_PENDING, |
234 | __LINK_STATE_DORMANT, | ||
234 | }; | 235 | }; |
235 | 236 | ||
236 | 237 | ||
@@ -335,11 +336,14 @@ struct net_device | |||
335 | */ | 336 | */ |
336 | 337 | ||
337 | 338 | ||
338 | unsigned short flags; /* interface flags (a la BSD) */ | 339 | unsigned int flags; /* interface flags (a la BSD) */ |
339 | unsigned short gflags; | 340 | unsigned short gflags; |
340 | unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */ | 341 | unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */ |
341 | unsigned short padded; /* How much padding added by alloc_netdev() */ | 342 | unsigned short padded; /* How much padding added by alloc_netdev() */ |
342 | 343 | ||
344 | unsigned char operstate; /* RFC2863 operstate */ | ||
345 | unsigned char link_mode; /* mapping policy to operstate */ | ||
346 | |||
343 | unsigned mtu; /* interface MTU value */ | 347 | unsigned mtu; /* interface MTU value */ |
344 | unsigned short type; /* interface hardware type */ | 348 | unsigned short type; /* interface hardware type */ |
345 | unsigned short hard_header_len; /* hardware hdr length */ | 349 | unsigned short hard_header_len; /* hardware hdr length */ |
@@ -714,6 +718,10 @@ static inline void dev_put(struct net_device *dev) | |||
714 | /* Carrier loss detection, dial on demand. The functions netif_carrier_on | 718 | /* Carrier loss detection, dial on demand. The functions netif_carrier_on |
715 | * and _off may be called from IRQ context, but it is caller | 719 | * and _off may be called from IRQ context, but it is caller |
716 | * who is responsible for serialization of these calls. | 720 | * who is responsible for serialization of these calls. |
721 | * | ||
722 | * The name carrier is inappropriate, these functions should really be | ||
723 | * called netif_lowerlayer_*() because they represent the state of any | ||
724 | * kind of lower layer not just hardware media. | ||
717 | */ | 725 | */ |
718 | 726 | ||
719 | extern void linkwatch_fire_event(struct net_device *dev); | 727 | extern void linkwatch_fire_event(struct net_device *dev); |
@@ -729,6 +737,29 @@ extern void netif_carrier_on(struct net_device *dev); | |||
729 | 737 | ||
730 | extern void netif_carrier_off(struct net_device *dev); | 738 | extern void netif_carrier_off(struct net_device *dev); |
731 | 739 | ||
740 | static inline void netif_dormant_on(struct net_device *dev) | ||
741 | { | ||
742 | if (!test_and_set_bit(__LINK_STATE_DORMANT, &dev->state)) | ||
743 | linkwatch_fire_event(dev); | ||
744 | } | ||
745 | |||
746 | static inline void netif_dormant_off(struct net_device *dev) | ||
747 | { | ||
748 | if (test_and_clear_bit(__LINK_STATE_DORMANT, &dev->state)) | ||
749 | linkwatch_fire_event(dev); | ||
750 | } | ||
751 | |||
752 | static inline int netif_dormant(const struct net_device *dev) | ||
753 | { | ||
754 | return test_bit(__LINK_STATE_DORMANT, &dev->state); | ||
755 | } | ||
756 | |||
757 | |||
758 | static inline int netif_oper_up(const struct net_device *dev) { | ||
759 | return (dev->operstate == IF_OPER_UP || | ||
760 | dev->operstate == IF_OPER_UNKNOWN /* backward compat */); | ||
761 | } | ||
762 | |||
732 | /* Hot-plugging. */ | 763 | /* Hot-plugging. */ |
733 | static inline int netif_device_present(struct net_device *dev) | 764 | static inline int netif_device_present(struct net_device *dev) |
734 | { | 765 | { |
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index d50482ba27fe..edccefb45188 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h | |||
@@ -733,6 +733,8 @@ enum | |||
733 | #define IFLA_MAP IFLA_MAP | 733 | #define IFLA_MAP IFLA_MAP |
734 | IFLA_WEIGHT, | 734 | IFLA_WEIGHT, |
735 | #define IFLA_WEIGHT IFLA_WEIGHT | 735 | #define IFLA_WEIGHT IFLA_WEIGHT |
736 | IFLA_OPERSTATE, | ||
737 | IFLA_LINKMODE, | ||
736 | __IFLA_MAX | 738 | __IFLA_MAX |
737 | }; | 739 | }; |
738 | 740 | ||
diff --git a/net/core/dev.c b/net/core/dev.c index ef56c035d44e..8763c99fcb84 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -2174,12 +2174,20 @@ unsigned dev_get_flags(const struct net_device *dev) | |||
2174 | 2174 | ||
2175 | flags = (dev->flags & ~(IFF_PROMISC | | 2175 | flags = (dev->flags & ~(IFF_PROMISC | |
2176 | IFF_ALLMULTI | | 2176 | IFF_ALLMULTI | |
2177 | IFF_RUNNING)) | | 2177 | IFF_RUNNING | |
2178 | IFF_LOWER_UP | | ||
2179 | IFF_DORMANT)) | | ||
2178 | (dev->gflags & (IFF_PROMISC | | 2180 | (dev->gflags & (IFF_PROMISC | |
2179 | IFF_ALLMULTI)); | 2181 | IFF_ALLMULTI)); |
2180 | 2182 | ||
2181 | if (netif_running(dev) && netif_carrier_ok(dev)) | 2183 | if (netif_running(dev)) { |
2182 | flags |= IFF_RUNNING; | 2184 | if (netif_oper_up(dev)) |
2185 | flags |= IFF_RUNNING; | ||
2186 | if (netif_carrier_ok(dev)) | ||
2187 | flags |= IFF_LOWER_UP; | ||
2188 | if (netif_dormant(dev)) | ||
2189 | flags |= IFF_DORMANT; | ||
2190 | } | ||
2183 | 2191 | ||
2184 | return flags; | 2192 | return flags; |
2185 | } | 2193 | } |
diff --git a/net/core/link_watch.c b/net/core/link_watch.c index d43d1201275c..e82a451d330b 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c | |||
@@ -49,6 +49,45 @@ struct lw_event { | |||
49 | /* Avoid kmalloc() for most systems */ | 49 | /* Avoid kmalloc() for most systems */ |
50 | static struct lw_event singleevent; | 50 | static struct lw_event singleevent; |
51 | 51 | ||
52 | static unsigned char default_operstate(const struct net_device *dev) | ||
53 | { | ||
54 | if (!netif_carrier_ok(dev)) | ||
55 | return (dev->ifindex != dev->iflink ? | ||
56 | IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN); | ||
57 | |||
58 | if (netif_dormant(dev)) | ||
59 | return IF_OPER_DORMANT; | ||
60 | |||
61 | return IF_OPER_UP; | ||
62 | } | ||
63 | |||
64 | |||
65 | static void rfc2863_policy(struct net_device *dev) | ||
66 | { | ||
67 | unsigned char operstate = default_operstate(dev); | ||
68 | |||
69 | if (operstate == dev->operstate) | ||
70 | return; | ||
71 | |||
72 | write_lock_bh(&dev_base_lock); | ||
73 | |||
74 | switch(dev->link_mode) { | ||
75 | case IF_LINK_MODE_DORMANT: | ||
76 | if (operstate == IF_OPER_UP) | ||
77 | operstate = IF_OPER_DORMANT; | ||
78 | break; | ||
79 | |||
80 | case IF_LINK_MODE_DEFAULT: | ||
81 | default: | ||
82 | break; | ||
83 | }; | ||
84 | |||
85 | dev->operstate = operstate; | ||
86 | |||
87 | write_unlock_bh(&dev_base_lock); | ||
88 | } | ||
89 | |||
90 | |||
52 | /* Must be called with the rtnl semaphore held */ | 91 | /* Must be called with the rtnl semaphore held */ |
53 | void linkwatch_run_queue(void) | 92 | void linkwatch_run_queue(void) |
54 | { | 93 | { |
@@ -74,6 +113,7 @@ void linkwatch_run_queue(void) | |||
74 | */ | 113 | */ |
75 | clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); | 114 | clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); |
76 | 115 | ||
116 | rfc2863_policy(dev); | ||
77 | if (dev->flags & IFF_UP) { | 117 | if (dev->flags & IFF_UP) { |
78 | if (netif_carrier_ok(dev)) { | 118 | if (netif_carrier_ok(dev)) { |
79 | WARN_ON(dev->qdisc_sleeping == &noop_qdisc); | 119 | WARN_ON(dev->qdisc_sleeping == &noop_qdisc); |
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index e8b2acbc8ea2..21b68464cabb 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c | |||
@@ -91,6 +91,7 @@ NETDEVICE_SHOW(iflink, fmt_dec); | |||
91 | NETDEVICE_SHOW(ifindex, fmt_dec); | 91 | NETDEVICE_SHOW(ifindex, fmt_dec); |
92 | NETDEVICE_SHOW(features, fmt_long_hex); | 92 | NETDEVICE_SHOW(features, fmt_long_hex); |
93 | NETDEVICE_SHOW(type, fmt_dec); | 93 | NETDEVICE_SHOW(type, fmt_dec); |
94 | NETDEVICE_SHOW(link_mode, fmt_dec); | ||
94 | 95 | ||
95 | /* use same locking rules as GIFHWADDR ioctl's */ | 96 | /* use same locking rules as GIFHWADDR ioctl's */ |
96 | static ssize_t format_addr(char *buf, const unsigned char *addr, int len) | 97 | static ssize_t format_addr(char *buf, const unsigned char *addr, int len) |
@@ -133,6 +134,43 @@ static ssize_t show_carrier(struct class_device *dev, char *buf) | |||
133 | return -EINVAL; | 134 | return -EINVAL; |
134 | } | 135 | } |
135 | 136 | ||
137 | static ssize_t show_dormant(struct class_device *dev, char *buf) | ||
138 | { | ||
139 | struct net_device *netdev = to_net_dev(dev); | ||
140 | |||
141 | if (netif_running(netdev)) | ||
142 | return sprintf(buf, fmt_dec, !!netif_dormant(netdev)); | ||
143 | |||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | static const char *operstates[] = { | ||
148 | "unknown", | ||
149 | "notpresent", /* currently unused */ | ||
150 | "down", | ||
151 | "lowerlayerdown", | ||
152 | "testing", /* currently unused */ | ||
153 | "dormant", | ||
154 | "up" | ||
155 | }; | ||
156 | |||
157 | static ssize_t show_operstate(struct class_device *dev, char *buf) | ||
158 | { | ||
159 | const struct net_device *netdev = to_net_dev(dev); | ||
160 | unsigned char operstate; | ||
161 | |||
162 | read_lock(&dev_base_lock); | ||
163 | operstate = netdev->operstate; | ||
164 | if (!netif_running(netdev)) | ||
165 | operstate = IF_OPER_DOWN; | ||
166 | read_unlock(&dev_base_lock); | ||
167 | |||
168 | if (operstate >= sizeof(operstates)) | ||
169 | return -EINVAL; /* should not happen */ | ||
170 | |||
171 | return sprintf(buf, "%s\n", operstates[operstate]); | ||
172 | } | ||
173 | |||
136 | /* read-write attributes */ | 174 | /* read-write attributes */ |
137 | NETDEVICE_SHOW(mtu, fmt_dec); | 175 | NETDEVICE_SHOW(mtu, fmt_dec); |
138 | 176 | ||
@@ -190,9 +228,12 @@ static struct class_device_attribute net_class_attributes[] = { | |||
190 | __ATTR(ifindex, S_IRUGO, show_ifindex, NULL), | 228 | __ATTR(ifindex, S_IRUGO, show_ifindex, NULL), |
191 | __ATTR(features, S_IRUGO, show_features, NULL), | 229 | __ATTR(features, S_IRUGO, show_features, NULL), |
192 | __ATTR(type, S_IRUGO, show_type, NULL), | 230 | __ATTR(type, S_IRUGO, show_type, NULL), |
231 | __ATTR(link_mode, S_IRUGO, show_link_mode, NULL), | ||
193 | __ATTR(address, S_IRUGO, show_address, NULL), | 232 | __ATTR(address, S_IRUGO, show_address, NULL), |
194 | __ATTR(broadcast, S_IRUGO, show_broadcast, NULL), | 233 | __ATTR(broadcast, S_IRUGO, show_broadcast, NULL), |
195 | __ATTR(carrier, S_IRUGO, show_carrier, NULL), | 234 | __ATTR(carrier, S_IRUGO, show_carrier, NULL), |
235 | __ATTR(dormant, S_IRUGO, show_dormant, NULL), | ||
236 | __ATTR(operstate, S_IRUGO, show_operstate, NULL), | ||
196 | __ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu), | 237 | __ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu), |
197 | __ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags), | 238 | __ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags), |
198 | __ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len, | 239 | __ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len, |
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index eca2976abb25..1c15a907066f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -179,6 +179,33 @@ rtattr_failure: | |||
179 | } | 179 | } |
180 | 180 | ||
181 | 181 | ||
182 | static void set_operstate(struct net_device *dev, unsigned char transition) | ||
183 | { | ||
184 | unsigned char operstate = dev->operstate; | ||
185 | |||
186 | switch(transition) { | ||
187 | case IF_OPER_UP: | ||
188 | if ((operstate == IF_OPER_DORMANT || | ||
189 | operstate == IF_OPER_UNKNOWN) && | ||
190 | !netif_dormant(dev)) | ||
191 | operstate = IF_OPER_UP; | ||
192 | break; | ||
193 | |||
194 | case IF_OPER_DORMANT: | ||
195 | if (operstate == IF_OPER_UP || | ||
196 | operstate == IF_OPER_UNKNOWN) | ||
197 | operstate = IF_OPER_DORMANT; | ||
198 | break; | ||
199 | }; | ||
200 | |||
201 | if (dev->operstate != operstate) { | ||
202 | write_lock_bh(&dev_base_lock); | ||
203 | dev->operstate = operstate; | ||
204 | write_unlock_bh(&dev_base_lock); | ||
205 | netdev_state_change(dev); | ||
206 | } | ||
207 | } | ||
208 | |||
182 | static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | 209 | static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, |
183 | int type, u32 pid, u32 seq, u32 change, | 210 | int type, u32 pid, u32 seq, u32 change, |
184 | unsigned int flags) | 211 | unsigned int flags) |
@@ -209,6 +236,13 @@ static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
209 | } | 236 | } |
210 | 237 | ||
211 | if (1) { | 238 | if (1) { |
239 | u8 operstate = netif_running(dev)?dev->operstate:IF_OPER_DOWN; | ||
240 | u8 link_mode = dev->link_mode; | ||
241 | RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate); | ||
242 | RTA_PUT(skb, IFLA_LINKMODE, sizeof(link_mode), &link_mode); | ||
243 | } | ||
244 | |||
245 | if (1) { | ||
212 | struct rtnl_link_ifmap map = { | 246 | struct rtnl_link_ifmap map = { |
213 | .mem_start = dev->mem_start, | 247 | .mem_start = dev->mem_start, |
214 | .mem_end = dev->mem_end, | 248 | .mem_end = dev->mem_end, |
@@ -399,6 +433,22 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
399 | dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); | 433 | dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); |
400 | } | 434 | } |
401 | 435 | ||
436 | if (ida[IFLA_OPERSTATE - 1]) { | ||
437 | if (ida[IFLA_OPERSTATE - 1]->rta_len != RTA_LENGTH(sizeof(u8))) | ||
438 | goto out; | ||
439 | |||
440 | set_operstate(dev, *((u8 *) RTA_DATA(ida[IFLA_OPERSTATE - 1]))); | ||
441 | } | ||
442 | |||
443 | if (ida[IFLA_LINKMODE - 1]) { | ||
444 | if (ida[IFLA_LINKMODE - 1]->rta_len != RTA_LENGTH(sizeof(u8))) | ||
445 | goto out; | ||
446 | |||
447 | write_lock_bh(&dev_base_lock); | ||
448 | dev->link_mode = *((u8 *) RTA_DATA(ida[IFLA_LINKMODE - 1])); | ||
449 | write_unlock_bh(&dev_base_lock); | ||
450 | } | ||
451 | |||
402 | if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) { | 452 | if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) { |
403 | char ifname[IFNAMSIZ]; | 453 | char ifname[IFNAMSIZ]; |
404 | 454 | ||