diff options
Diffstat (limited to 'net/hsr/hsr_slave.c')
-rw-r--r-- | net/hsr/hsr_slave.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c new file mode 100644 index 000000000000..a348dcbcd683 --- /dev/null +++ b/net/hsr/hsr_slave.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* Copyright 2011-2014 Autronica Fire and Security AS | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify it | ||
4 | * under the terms of the GNU General Public License as published by the Free | ||
5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
6 | * any later version. | ||
7 | * | ||
8 | * Author(s): | ||
9 | * 2011-2014 Arvid Brodin, arvid.brodin@alten.se | ||
10 | */ | ||
11 | |||
12 | #include "hsr_slave.h" | ||
13 | #include <linux/etherdevice.h> | ||
14 | #include <linux/if_arp.h> | ||
15 | #include "hsr_main.h" | ||
16 | #include "hsr_device.h" | ||
17 | #include "hsr_forward.h" | ||
18 | #include "hsr_framereg.h" | ||
19 | |||
20 | |||
21 | static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) | ||
22 | { | ||
23 | struct sk_buff *skb = *pskb; | ||
24 | struct hsr_port *port; | ||
25 | |||
26 | if (!skb_mac_header_was_set(skb)) { | ||
27 | WARN_ONCE(1, "%s: skb invalid", __func__); | ||
28 | return RX_HANDLER_PASS; | ||
29 | } | ||
30 | |||
31 | rcu_read_lock(); /* hsr->node_db, hsr->ports */ | ||
32 | port = hsr_port_get_rcu(skb->dev); | ||
33 | |||
34 | if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) { | ||
35 | /* Directly kill frames sent by ourselves */ | ||
36 | kfree_skb(skb); | ||
37 | goto finish_consume; | ||
38 | } | ||
39 | |||
40 | if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP)) | ||
41 | goto finish_pass; | ||
42 | |||
43 | skb_push(skb, ETH_HLEN); | ||
44 | |||
45 | hsr_forward_skb(skb, port); | ||
46 | |||
47 | finish_consume: | ||
48 | rcu_read_unlock(); /* hsr->node_db, hsr->ports */ | ||
49 | return RX_HANDLER_CONSUMED; | ||
50 | |||
51 | finish_pass: | ||
52 | rcu_read_unlock(); /* hsr->node_db, hsr->ports */ | ||
53 | return RX_HANDLER_PASS; | ||
54 | } | ||
55 | |||
56 | bool hsr_port_exists(const struct net_device *dev) | ||
57 | { | ||
58 | return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame; | ||
59 | } | ||
60 | |||
61 | |||
62 | static int hsr_check_dev_ok(struct net_device *dev) | ||
63 | { | ||
64 | /* Don't allow HSR on non-ethernet like devices */ | ||
65 | if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || | ||
66 | (dev->addr_len != ETH_ALEN)) { | ||
67 | netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); | ||
68 | return -EINVAL; | ||
69 | } | ||
70 | |||
71 | /* Don't allow enslaving hsr devices */ | ||
72 | if (is_hsr_master(dev)) { | ||
73 | netdev_info(dev, "Cannot create trees of HSR devices.\n"); | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | |||
77 | if (hsr_port_exists(dev)) { | ||
78 | netdev_info(dev, "This device is already a HSR slave.\n"); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | if (dev->priv_flags & IFF_802_1Q_VLAN) { | ||
83 | netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); | ||
84 | return -EINVAL; | ||
85 | } | ||
86 | |||
87 | if (dev->priv_flags & IFF_DONT_BRIDGE) { | ||
88 | netdev_info(dev, "This device does not support bridging.\n"); | ||
89 | return -EOPNOTSUPP; | ||
90 | } | ||
91 | |||
92 | /* HSR over bonded devices has not been tested, but I'm not sure it | ||
93 | * won't work... | ||
94 | */ | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | |||
100 | /* Setup device to be added to the HSR bridge. */ | ||
101 | static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port) | ||
102 | { | ||
103 | int res; | ||
104 | |||
105 | dev_hold(dev); | ||
106 | res = dev_set_promiscuity(dev, 1); | ||
107 | if (res) | ||
108 | goto fail_promiscuity; | ||
109 | |||
110 | /* FIXME: | ||
111 | * What does net device "adjacency" mean? Should we do | ||
112 | * res = netdev_master_upper_dev_link(port->dev, port->hsr->dev); ? | ||
113 | */ | ||
114 | |||
115 | res = netdev_rx_handler_register(dev, hsr_handle_frame, port); | ||
116 | if (res) | ||
117 | goto fail_rx_handler; | ||
118 | dev_disable_lro(dev); | ||
119 | |||
120 | return 0; | ||
121 | |||
122 | fail_rx_handler: | ||
123 | dev_set_promiscuity(dev, -1); | ||
124 | fail_promiscuity: | ||
125 | dev_put(dev); | ||
126 | |||
127 | return res; | ||
128 | } | ||
129 | |||
130 | int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, | ||
131 | enum hsr_port_type type) | ||
132 | { | ||
133 | struct hsr_port *port, *master; | ||
134 | int res; | ||
135 | |||
136 | if (type != HSR_PT_MASTER) { | ||
137 | res = hsr_check_dev_ok(dev); | ||
138 | if (res) | ||
139 | return res; | ||
140 | } | ||
141 | |||
142 | port = hsr_port_get_hsr(hsr, type); | ||
143 | if (port != NULL) | ||
144 | return -EBUSY; /* This port already exists */ | ||
145 | |||
146 | port = kzalloc(sizeof(*port), GFP_KERNEL); | ||
147 | if (port == NULL) | ||
148 | return -ENOMEM; | ||
149 | |||
150 | if (type != HSR_PT_MASTER) { | ||
151 | res = hsr_portdev_setup(dev, port); | ||
152 | if (res) | ||
153 | goto fail_dev_setup; | ||
154 | } | ||
155 | |||
156 | port->hsr = hsr; | ||
157 | port->dev = dev; | ||
158 | port->type = type; | ||
159 | |||
160 | list_add_tail_rcu(&port->port_list, &hsr->ports); | ||
161 | synchronize_rcu(); | ||
162 | |||
163 | master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); | ||
164 | netdev_update_features(master->dev); | ||
165 | dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); | ||
166 | |||
167 | return 0; | ||
168 | |||
169 | fail_dev_setup: | ||
170 | kfree(port); | ||
171 | return res; | ||
172 | } | ||
173 | |||
174 | void hsr_del_port(struct hsr_port *port) | ||
175 | { | ||
176 | struct hsr_priv *hsr; | ||
177 | struct hsr_port *master; | ||
178 | |||
179 | hsr = port->hsr; | ||
180 | master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); | ||
181 | list_del_rcu(&port->port_list); | ||
182 | |||
183 | if (port != master) { | ||
184 | netdev_update_features(master->dev); | ||
185 | dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); | ||
186 | netdev_rx_handler_unregister(port->dev); | ||
187 | dev_set_promiscuity(port->dev, -1); | ||
188 | } | ||
189 | |||
190 | /* FIXME? | ||
191 | * netdev_upper_dev_unlink(port->dev, port->hsr->dev); | ||
192 | */ | ||
193 | |||
194 | synchronize_rcu(); | ||
195 | dev_put(port->dev); | ||
196 | } | ||