1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/proc_fs.h> |
3 | #include <linux/ethtool.h> |
4 | #include <linux/export.h> |
5 | #include <net/net_namespace.h> |
6 | #include <net/netns/generic.h> |
7 | #include <net/bonding.h> |
8 | |
9 | #include "bonding_priv.h" |
10 | |
11 | static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos) |
12 | __acquires(RCU) |
13 | { |
14 | struct bonding *bond = pde_data(inode: file_inode(f: seq->file)); |
15 | struct list_head *iter; |
16 | struct slave *slave; |
17 | loff_t off = 0; |
18 | |
19 | rcu_read_lock(); |
20 | |
21 | if (*pos == 0) |
22 | return SEQ_START_TOKEN; |
23 | |
24 | bond_for_each_slave_rcu(bond, slave, iter) |
25 | if (++off == *pos) |
26 | return slave; |
27 | |
28 | return NULL; |
29 | } |
30 | |
31 | static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
32 | { |
33 | struct bonding *bond = pde_data(inode: file_inode(f: seq->file)); |
34 | struct list_head *iter; |
35 | struct slave *slave; |
36 | bool found = false; |
37 | |
38 | ++*pos; |
39 | if (v == SEQ_START_TOKEN) |
40 | return bond_first_slave_rcu(bond); |
41 | |
42 | bond_for_each_slave_rcu(bond, slave, iter) { |
43 | if (found) |
44 | return slave; |
45 | if (slave == v) |
46 | found = true; |
47 | } |
48 | |
49 | return NULL; |
50 | } |
51 | |
52 | static void bond_info_seq_stop(struct seq_file *seq, void *v) |
53 | __releases(RCU) |
54 | { |
55 | rcu_read_unlock(); |
56 | } |
57 | |
58 | static void bond_info_show_master(struct seq_file *seq) |
59 | { |
60 | struct bonding *bond = pde_data(inode: file_inode(f: seq->file)); |
61 | const struct bond_opt_value *optval; |
62 | struct slave *curr, *primary; |
63 | int i; |
64 | |
65 | curr = rcu_dereference(bond->curr_active_slave); |
66 | |
67 | seq_printf(m: seq, fmt: "Bonding Mode: %s" , |
68 | bond_mode_name(BOND_MODE(bond))); |
69 | |
70 | if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP && |
71 | bond->params.fail_over_mac) { |
72 | optval = bond_opt_get_val(option: BOND_OPT_FAIL_OVER_MAC, |
73 | val: bond->params.fail_over_mac); |
74 | seq_printf(m: seq, fmt: " (fail_over_mac %s)" , optval->string); |
75 | } |
76 | |
77 | seq_printf(m: seq, fmt: "\n" ); |
78 | |
79 | if (bond_mode_uses_xmit_hash(bond)) { |
80 | optval = bond_opt_get_val(option: BOND_OPT_XMIT_HASH, |
81 | val: bond->params.xmit_policy); |
82 | seq_printf(m: seq, fmt: "Transmit Hash Policy: %s (%d)\n" , |
83 | optval->string, bond->params.xmit_policy); |
84 | } |
85 | |
86 | if (bond_uses_primary(bond)) { |
87 | primary = rcu_dereference(bond->primary_slave); |
88 | seq_printf(m: seq, fmt: "Primary Slave: %s" , |
89 | primary ? primary->dev->name : "None" ); |
90 | if (primary) { |
91 | optval = bond_opt_get_val(option: BOND_OPT_PRIMARY_RESELECT, |
92 | val: bond->params.primary_reselect); |
93 | seq_printf(m: seq, fmt: " (primary_reselect %s)" , |
94 | optval->string); |
95 | } |
96 | |
97 | seq_printf(m: seq, fmt: "\nCurrently Active Slave: %s\n" , |
98 | (curr) ? curr->dev->name : "None" ); |
99 | } |
100 | |
101 | seq_printf(m: seq, fmt: "MII Status: %s\n" , netif_carrier_ok(dev: bond->dev) ? |
102 | "up" : "down" ); |
103 | seq_printf(m: seq, fmt: "MII Polling Interval (ms): %d\n" , bond->params.miimon); |
104 | seq_printf(m: seq, fmt: "Up Delay (ms): %d\n" , |
105 | bond->params.updelay * bond->params.miimon); |
106 | seq_printf(m: seq, fmt: "Down Delay (ms): %d\n" , |
107 | bond->params.downdelay * bond->params.miimon); |
108 | seq_printf(m: seq, fmt: "Peer Notification Delay (ms): %d\n" , |
109 | bond->params.peer_notif_delay * bond->params.miimon); |
110 | |
111 | |
112 | /* ARP information */ |
113 | if (bond->params.arp_interval > 0) { |
114 | int printed = 0; |
115 | |
116 | seq_printf(m: seq, fmt: "ARP Polling Interval (ms): %d\n" , |
117 | bond->params.arp_interval); |
118 | seq_printf(m: seq, fmt: "ARP Missed Max: %u\n" , |
119 | bond->params.missed_max); |
120 | |
121 | seq_printf(m: seq, fmt: "ARP IP target/s (n.n.n.n form):" ); |
122 | |
123 | for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) { |
124 | if (!bond->params.arp_targets[i]) |
125 | break; |
126 | if (printed) |
127 | seq_printf(m: seq, fmt: "," ); |
128 | seq_printf(m: seq, fmt: " %pI4" , &bond->params.arp_targets[i]); |
129 | printed = 1; |
130 | } |
131 | seq_printf(m: seq, fmt: "\n" ); |
132 | |
133 | #if IS_ENABLED(CONFIG_IPV6) |
134 | printed = 0; |
135 | seq_printf(m: seq, fmt: "NS IPv6 target/s (xx::xx form):" ); |
136 | |
137 | for (i = 0; (i < BOND_MAX_NS_TARGETS); i++) { |
138 | if (ipv6_addr_any(a: &bond->params.ns_targets[i])) |
139 | break; |
140 | if (printed) |
141 | seq_printf(m: seq, fmt: "," ); |
142 | seq_printf(m: seq, fmt: " %pI6c" , &bond->params.ns_targets[i]); |
143 | printed = 1; |
144 | } |
145 | seq_printf(m: seq, fmt: "\n" ); |
146 | #endif |
147 | } |
148 | |
149 | if (BOND_MODE(bond) == BOND_MODE_8023AD) { |
150 | struct ad_info ad_info; |
151 | |
152 | seq_puts(m: seq, s: "\n802.3ad info\n" ); |
153 | seq_printf(m: seq, fmt: "LACP active: %s\n" , |
154 | (bond->params.lacp_active) ? "on" : "off" ); |
155 | seq_printf(m: seq, fmt: "LACP rate: %s\n" , |
156 | (bond->params.lacp_fast) ? "fast" : "slow" ); |
157 | seq_printf(m: seq, fmt: "Min links: %d\n" , bond->params.min_links); |
158 | optval = bond_opt_get_val(option: BOND_OPT_AD_SELECT, |
159 | val: bond->params.ad_select); |
160 | seq_printf(m: seq, fmt: "Aggregator selection policy (ad_select): %s\n" , |
161 | optval->string); |
162 | if (capable(CAP_NET_ADMIN)) { |
163 | seq_printf(m: seq, fmt: "System priority: %d\n" , |
164 | BOND_AD_INFO(bond).system.sys_priority); |
165 | seq_printf(m: seq, fmt: "System MAC address: %pM\n" , |
166 | &BOND_AD_INFO(bond).system.sys_mac_addr); |
167 | |
168 | if (__bond_3ad_get_active_agg_info(bond, ad_info: &ad_info)) { |
169 | seq_printf(m: seq, |
170 | fmt: "bond %s has no active aggregator\n" , |
171 | bond->dev->name); |
172 | } else { |
173 | seq_printf(m: seq, fmt: "Active Aggregator Info:\n" ); |
174 | |
175 | seq_printf(m: seq, fmt: "\tAggregator ID: %d\n" , |
176 | ad_info.aggregator_id); |
177 | seq_printf(m: seq, fmt: "\tNumber of ports: %d\n" , |
178 | ad_info.ports); |
179 | seq_printf(m: seq, fmt: "\tActor Key: %d\n" , |
180 | ad_info.actor_key); |
181 | seq_printf(m: seq, fmt: "\tPartner Key: %d\n" , |
182 | ad_info.partner_key); |
183 | seq_printf(m: seq, fmt: "\tPartner Mac Address: %pM\n" , |
184 | ad_info.partner_system); |
185 | } |
186 | } |
187 | } |
188 | } |
189 | |
190 | static void bond_info_show_slave(struct seq_file *seq, |
191 | const struct slave *slave) |
192 | { |
193 | struct bonding *bond = pde_data(inode: file_inode(f: seq->file)); |
194 | |
195 | seq_printf(m: seq, fmt: "\nSlave Interface: %s\n" , slave->dev->name); |
196 | seq_printf(m: seq, fmt: "MII Status: %s\n" , bond_slave_link_status(link: slave->link)); |
197 | if (slave->speed == SPEED_UNKNOWN) |
198 | seq_printf(m: seq, fmt: "Speed: %s\n" , "Unknown" ); |
199 | else |
200 | seq_printf(m: seq, fmt: "Speed: %d Mbps\n" , slave->speed); |
201 | |
202 | if (slave->duplex == DUPLEX_UNKNOWN) |
203 | seq_printf(m: seq, fmt: "Duplex: %s\n" , "Unknown" ); |
204 | else |
205 | seq_printf(m: seq, fmt: "Duplex: %s\n" , slave->duplex ? "full" : "half" ); |
206 | |
207 | seq_printf(m: seq, fmt: "Link Failure Count: %u\n" , |
208 | slave->link_failure_count); |
209 | |
210 | seq_printf(m: seq, fmt: "Permanent HW addr: %*phC\n" , |
211 | slave->dev->addr_len, slave->perm_hwaddr); |
212 | seq_printf(m: seq, fmt: "Slave queue ID: %d\n" , slave->queue_id); |
213 | |
214 | if (BOND_MODE(bond) == BOND_MODE_8023AD) { |
215 | const struct port *port = &SLAVE_AD_INFO(slave)->port; |
216 | const struct aggregator *agg = port->aggregator; |
217 | |
218 | if (agg) { |
219 | seq_printf(m: seq, fmt: "Aggregator ID: %d\n" , |
220 | agg->aggregator_identifier); |
221 | seq_printf(m: seq, fmt: "Actor Churn State: %s\n" , |
222 | bond_3ad_churn_desc(state: port->sm_churn_actor_state)); |
223 | seq_printf(m: seq, fmt: "Partner Churn State: %s\n" , |
224 | bond_3ad_churn_desc(state: port->sm_churn_partner_state)); |
225 | seq_printf(m: seq, fmt: "Actor Churned Count: %d\n" , |
226 | port->churn_actor_count); |
227 | seq_printf(m: seq, fmt: "Partner Churned Count: %d\n" , |
228 | port->churn_partner_count); |
229 | |
230 | if (capable(CAP_NET_ADMIN)) { |
231 | seq_puts(m: seq, s: "details actor lacp pdu:\n" ); |
232 | seq_printf(m: seq, fmt: " system priority: %d\n" , |
233 | port->actor_system_priority); |
234 | seq_printf(m: seq, fmt: " system mac address: %pM\n" , |
235 | &port->actor_system); |
236 | seq_printf(m: seq, fmt: " port key: %d\n" , |
237 | port->actor_oper_port_key); |
238 | seq_printf(m: seq, fmt: " port priority: %d\n" , |
239 | port->actor_port_priority); |
240 | seq_printf(m: seq, fmt: " port number: %d\n" , |
241 | port->actor_port_number); |
242 | seq_printf(m: seq, fmt: " port state: %d\n" , |
243 | port->actor_oper_port_state); |
244 | |
245 | seq_puts(m: seq, s: "details partner lacp pdu:\n" ); |
246 | seq_printf(m: seq, fmt: " system priority: %d\n" , |
247 | port->partner_oper.system_priority); |
248 | seq_printf(m: seq, fmt: " system mac address: %pM\n" , |
249 | &port->partner_oper.system); |
250 | seq_printf(m: seq, fmt: " oper key: %d\n" , |
251 | port->partner_oper.key); |
252 | seq_printf(m: seq, fmt: " port priority: %d\n" , |
253 | port->partner_oper.port_priority); |
254 | seq_printf(m: seq, fmt: " port number: %d\n" , |
255 | port->partner_oper.port_number); |
256 | seq_printf(m: seq, fmt: " port state: %d\n" , |
257 | port->partner_oper.port_state); |
258 | } |
259 | } else { |
260 | seq_puts(m: seq, s: "Aggregator ID: N/A\n" ); |
261 | } |
262 | } |
263 | } |
264 | |
265 | static int bond_info_seq_show(struct seq_file *seq, void *v) |
266 | { |
267 | if (v == SEQ_START_TOKEN) { |
268 | seq_printf(m: seq, fmt: "%s\n" , bond_version); |
269 | bond_info_show_master(seq); |
270 | } else |
271 | bond_info_show_slave(seq, slave: v); |
272 | |
273 | return 0; |
274 | } |
275 | |
276 | static const struct seq_operations bond_info_seq_ops = { |
277 | .start = bond_info_seq_start, |
278 | .next = bond_info_seq_next, |
279 | .stop = bond_info_seq_stop, |
280 | .show = bond_info_seq_show, |
281 | }; |
282 | |
283 | void bond_create_proc_entry(struct bonding *bond) |
284 | { |
285 | struct net_device *bond_dev = bond->dev; |
286 | struct bond_net *bn = net_generic(net: dev_net(dev: bond_dev), id: bond_net_id); |
287 | |
288 | if (bn->proc_dir) { |
289 | bond->proc_entry = proc_create_seq_data(bond_dev->name, 0444, |
290 | bn->proc_dir, &bond_info_seq_ops, bond); |
291 | if (bond->proc_entry == NULL) |
292 | netdev_warn(dev: bond_dev, format: "Cannot create /proc/net/%s/%s\n" , |
293 | DRV_NAME, bond_dev->name); |
294 | else |
295 | memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ); |
296 | } |
297 | } |
298 | |
299 | void bond_remove_proc_entry(struct bonding *bond) |
300 | { |
301 | struct net_device *bond_dev = bond->dev; |
302 | struct bond_net *bn = net_generic(net: dev_net(dev: bond_dev), id: bond_net_id); |
303 | |
304 | if (bn->proc_dir && bond->proc_entry) { |
305 | remove_proc_entry(bond->proc_file_name, bn->proc_dir); |
306 | memset(bond->proc_file_name, 0, IFNAMSIZ); |
307 | bond->proc_entry = NULL; |
308 | } |
309 | } |
310 | |
311 | /* Create the bonding directory under /proc/net, if doesn't exist yet. |
312 | * Caller must hold rtnl_lock. |
313 | */ |
314 | void __net_init bond_create_proc_dir(struct bond_net *bn) |
315 | { |
316 | if (!bn->proc_dir) { |
317 | bn->proc_dir = proc_mkdir(DRV_NAME, bn->net->proc_net); |
318 | if (!bn->proc_dir) |
319 | pr_warn("Warning: Cannot create /proc/net/%s\n" , |
320 | DRV_NAME); |
321 | } |
322 | } |
323 | |
324 | /* Destroy the bonding directory under /proc/net, if empty. |
325 | */ |
326 | void __net_exit bond_destroy_proc_dir(struct bond_net *bn) |
327 | { |
328 | if (bn->proc_dir) { |
329 | remove_proc_entry(DRV_NAME, bn->net->proc_net); |
330 | bn->proc_dir = NULL; |
331 | } |
332 | } |
333 | |