bridge: cfm: Kernel space implementation of CFM. CCM frame TX added.
[linux-block.git] / net / bridge / br_cfm.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 #include <linux/cfm_bridge.h>
4 #include <uapi/linux/cfm_bridge.h>
5 #include "br_private_cfm.h"
6
7 static struct br_cfm_mep *br_mep_find(struct net_bridge *br, u32 instance)
8 {
9         struct br_cfm_mep *mep;
10
11         hlist_for_each_entry(mep, &br->mep_list, head)
12                 if (mep->instance == instance)
13                         return mep;
14
15         return NULL;
16 }
17
18 static struct br_cfm_mep *br_mep_find_ifindex(struct net_bridge *br,
19                                               u32 ifindex)
20 {
21         struct br_cfm_mep *mep;
22
23         hlist_for_each_entry_rcu(mep, &br->mep_list, head,
24                                  lockdep_rtnl_is_held())
25                 if (mep->create.ifindex == ifindex)
26                         return mep;
27
28         return NULL;
29 }
30
31 static struct br_cfm_peer_mep *br_peer_mep_find(struct br_cfm_mep *mep,
32                                                 u32 mepid)
33 {
34         struct br_cfm_peer_mep *peer_mep;
35
36         hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head,
37                                  lockdep_rtnl_is_held())
38                 if (peer_mep->mepid == mepid)
39                         return peer_mep;
40
41         return NULL;
42 }
43
44 static struct net_bridge_port *br_mep_get_port(struct net_bridge *br,
45                                                u32 ifindex)
46 {
47         struct net_bridge_port *port;
48
49         list_for_each_entry(port, &br->port_list, list)
50                 if (port->dev->ifindex == ifindex)
51                         return port;
52
53         return NULL;
54 }
55
56 /* Calculate the CCM interval in us. */
57 static u32 interval_to_us(enum br_cfm_ccm_interval interval)
58 {
59         switch (interval) {
60         case BR_CFM_CCM_INTERVAL_NONE:
61                 return 0;
62         case BR_CFM_CCM_INTERVAL_3_3_MS:
63                 return 3300;
64         case BR_CFM_CCM_INTERVAL_10_MS:
65                 return 10 * 1000;
66         case BR_CFM_CCM_INTERVAL_100_MS:
67                 return 100 * 1000;
68         case BR_CFM_CCM_INTERVAL_1_SEC:
69                 return 1000 * 1000;
70         case BR_CFM_CCM_INTERVAL_10_SEC:
71                 return 10 * 1000 * 1000;
72         case BR_CFM_CCM_INTERVAL_1_MIN:
73                 return 60 * 1000 * 1000;
74         case BR_CFM_CCM_INTERVAL_10_MIN:
75                 return 10 * 60 * 1000 * 1000;
76         }
77         return 0;
78 }
79
80 /* Convert the interface interval to CCM PDU value. */
81 static u32 interval_to_pdu(enum br_cfm_ccm_interval interval)
82 {
83         switch (interval) {
84         case BR_CFM_CCM_INTERVAL_NONE:
85                 return 0;
86         case BR_CFM_CCM_INTERVAL_3_3_MS:
87                 return 1;
88         case BR_CFM_CCM_INTERVAL_10_MS:
89                 return 2;
90         case BR_CFM_CCM_INTERVAL_100_MS:
91                 return 3;
92         case BR_CFM_CCM_INTERVAL_1_SEC:
93                 return 4;
94         case BR_CFM_CCM_INTERVAL_10_SEC:
95                 return 5;
96         case BR_CFM_CCM_INTERVAL_1_MIN:
97                 return 6;
98         case BR_CFM_CCM_INTERVAL_10_MIN:
99                 return 7;
100         }
101         return 0;
102 }
103
104 static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
105                                        const struct br_cfm_cc_ccm_tx_info *const tx_info)
106
107 {
108         struct br_cfm_common_hdr *common_hdr;
109         struct net_bridge_port *b_port;
110         struct br_cfm_maid *maid;
111         u8 *itu_reserved, *e_tlv;
112         struct ethhdr *eth_hdr;
113         struct sk_buff *skb;
114         __be32 *status_tlv;
115         __be32 *snumber;
116         __be16 *mepid;
117
118         skb = dev_alloc_skb(CFM_CCM_MAX_FRAME_LENGTH);
119         if (!skb)
120                 return NULL;
121
122         rcu_read_lock();
123         b_port = rcu_dereference(mep->b_port);
124         if (!b_port) {
125                 kfree_skb(skb);
126                 rcu_read_unlock();
127                 return NULL;
128         }
129         skb->dev = b_port->dev;
130         rcu_read_unlock();
131         /* The device cannot be deleted until the work_queue functions has
132          * completed. This function is called from ccm_tx_work_expired()
133          * that is a work_queue functions.
134          */
135
136         skb->protocol = htons(ETH_P_CFM);
137         skb->priority = CFM_FRAME_PRIO;
138
139         /* Ethernet header */
140         eth_hdr = skb_put(skb, sizeof(*eth_hdr));
141         ether_addr_copy(eth_hdr->h_dest, tx_info->dmac.addr);
142         ether_addr_copy(eth_hdr->h_source, mep->config.unicast_mac.addr);
143         eth_hdr->h_proto = htons(ETH_P_CFM);
144
145         /* Common CFM Header */
146         common_hdr = skb_put(skb, sizeof(*common_hdr));
147         common_hdr->mdlevel_version = mep->config.mdlevel << 5;
148         common_hdr->opcode = BR_CFM_OPCODE_CCM;
149         common_hdr->flags = (mep->rdi << 7) |
150                             interval_to_pdu(mep->cc_config.exp_interval);
151         common_hdr->tlv_offset = CFM_CCM_TLV_OFFSET;
152
153         /* Sequence number */
154         snumber = skb_put(skb, sizeof(*snumber));
155         if (tx_info->seq_no_update) {
156                 *snumber = cpu_to_be32(mep->ccm_tx_snumber);
157                 mep->ccm_tx_snumber += 1;
158         } else {
159                 *snumber = 0;
160         }
161
162         mepid = skb_put(skb, sizeof(*mepid));
163         *mepid = cpu_to_be16((u16)mep->config.mepid);
164
165         maid = skb_put(skb, sizeof(*maid));
166         memcpy(maid->data, mep->cc_config.exp_maid.data, sizeof(maid->data));
167
168         /* ITU reserved (CFM_CCM_ITU_RESERVED_SIZE octets) */
169         itu_reserved = skb_put(skb, CFM_CCM_ITU_RESERVED_SIZE);
170         memset(itu_reserved, 0, CFM_CCM_ITU_RESERVED_SIZE);
171
172         /* Generel CFM TLV format:
173          * TLV type:            one byte
174          * TLV value length:    two bytes
175          * TLV value:           'TLV value length' bytes
176          */
177
178         /* Port status TLV. The value length is 1. Total of 4 bytes. */
179         if (tx_info->port_tlv) {
180                 status_tlv = skb_put(skb, sizeof(*status_tlv));
181                 *status_tlv = cpu_to_be32((CFM_PORT_STATUS_TLV_TYPE << 24) |
182                                           (1 << 8) |    /* Value length */
183                                           (tx_info->port_tlv_value & 0xFF));
184         }
185
186         /* Interface status TLV. The value length is 1. Total of 4 bytes. */
187         if (tx_info->if_tlv) {
188                 status_tlv = skb_put(skb, sizeof(*status_tlv));
189                 *status_tlv = cpu_to_be32((CFM_IF_STATUS_TLV_TYPE << 24) |
190                                           (1 << 8) |    /* Value length */
191                                           (tx_info->if_tlv_value & 0xFF));
192         }
193
194         /* End TLV */
195         e_tlv = skb_put(skb, sizeof(*e_tlv));
196         *e_tlv = CFM_ENDE_TLV_TYPE;
197
198         return skb;
199 }
200
201 static void ccm_frame_tx(struct sk_buff *skb)
202 {
203         skb_reset_network_header(skb);
204         dev_queue_xmit(skb);
205 }
206
207 /* This function is called with the configured CC 'expected_interval'
208  * in order to drive CCM transmission when enabled.
209  */
210 static void ccm_tx_work_expired(struct work_struct *work)
211 {
212         struct delayed_work *del_work;
213         struct br_cfm_mep *mep;
214         struct sk_buff *skb;
215         u32 interval_us;
216
217         del_work = to_delayed_work(work);
218         mep = container_of(del_work, struct br_cfm_mep, ccm_tx_dwork);
219
220         if (time_before_eq(mep->ccm_tx_end, jiffies)) {
221                 /* Transmission period has ended */
222                 mep->cc_ccm_tx_info.period = 0;
223                 return;
224         }
225
226         skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
227         if (skb)
228                 ccm_frame_tx(skb);
229
230         interval_us = interval_to_us(mep->cc_config.exp_interval);
231         queue_delayed_work(system_wq, &mep->ccm_tx_dwork,
232                            usecs_to_jiffies(interval_us));
233 }
234
235 int br_cfm_mep_create(struct net_bridge *br,
236                       const u32 instance,
237                       struct br_cfm_mep_create *const create,
238                       struct netlink_ext_ack *extack)
239 {
240         struct net_bridge_port *p;
241         struct br_cfm_mep *mep;
242
243         ASSERT_RTNL();
244
245         if (create->domain == BR_CFM_VLAN) {
246                 NL_SET_ERR_MSG_MOD(extack,
247                                    "VLAN domain not supported");
248                 return -EINVAL;
249         }
250         if (create->domain != BR_CFM_PORT) {
251                 NL_SET_ERR_MSG_MOD(extack,
252                                    "Invalid domain value");
253                 return -EINVAL;
254         }
255         if (create->direction == BR_CFM_MEP_DIRECTION_UP) {
256                 NL_SET_ERR_MSG_MOD(extack,
257                                    "Up-MEP not supported");
258                 return -EINVAL;
259         }
260         if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) {
261                 NL_SET_ERR_MSG_MOD(extack,
262                                    "Invalid direction value");
263                 return -EINVAL;
264         }
265         p = br_mep_get_port(br, create->ifindex);
266         if (!p) {
267                 NL_SET_ERR_MSG_MOD(extack,
268                                    "Port is not related to bridge");
269                 return -EINVAL;
270         }
271         mep = br_mep_find(br, instance);
272         if (mep) {
273                 NL_SET_ERR_MSG_MOD(extack,
274                                    "MEP instance already exists");
275                 return -EEXIST;
276         }
277
278         /* In PORT domain only one instance can be created per port */
279         if (create->domain == BR_CFM_PORT) {
280                 mep = br_mep_find_ifindex(br, create->ifindex);
281                 if (mep) {
282                         NL_SET_ERR_MSG_MOD(extack,
283                                            "Only one Port MEP on a port allowed");
284                         return -EINVAL;
285                 }
286         }
287
288         mep = kzalloc(sizeof(*mep), GFP_KERNEL);
289         if (!mep)
290                 return -ENOMEM;
291
292         mep->create = *create;
293         mep->instance = instance;
294         rcu_assign_pointer(mep->b_port, p);
295
296         INIT_HLIST_HEAD(&mep->peer_mep_list);
297         INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
298
299         hlist_add_tail_rcu(&mep->head, &br->mep_list);
300
301         return 0;
302 }
303
304 static void mep_delete_implementation(struct net_bridge *br,
305                                       struct br_cfm_mep *mep)
306 {
307         struct br_cfm_peer_mep *peer_mep;
308         struct hlist_node *n_store;
309
310         ASSERT_RTNL();
311
312         /* Empty and free peer MEP list */
313         hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) {
314                 hlist_del_rcu(&peer_mep->head);
315                 kfree_rcu(peer_mep, rcu);
316         }
317
318         cancel_delayed_work_sync(&mep->ccm_tx_dwork);
319
320         RCU_INIT_POINTER(mep->b_port, NULL);
321         hlist_del_rcu(&mep->head);
322         kfree_rcu(mep, rcu);
323 }
324
325 int br_cfm_mep_delete(struct net_bridge *br,
326                       const u32 instance,
327                       struct netlink_ext_ack *extack)
328 {
329         struct br_cfm_mep *mep;
330
331         ASSERT_RTNL();
332
333         mep = br_mep_find(br, instance);
334         if (!mep) {
335                 NL_SET_ERR_MSG_MOD(extack,
336                                    "MEP instance does not exists");
337                 return -ENOENT;
338         }
339
340         mep_delete_implementation(br, mep);
341
342         return 0;
343 }
344
345 int br_cfm_mep_config_set(struct net_bridge *br,
346                           const u32 instance,
347                           const struct br_cfm_mep_config *const config,
348                           struct netlink_ext_ack *extack)
349 {
350         struct br_cfm_mep *mep;
351
352         ASSERT_RTNL();
353
354         mep = br_mep_find(br, instance);
355         if (!mep) {
356                 NL_SET_ERR_MSG_MOD(extack,
357                                    "MEP instance does not exists");
358                 return -ENOENT;
359         }
360
361         mep->config = *config;
362
363         return 0;
364 }
365
366 int br_cfm_cc_config_set(struct net_bridge *br,
367                          const u32 instance,
368                          const struct br_cfm_cc_config *const config,
369                          struct netlink_ext_ack *extack)
370 {
371         struct br_cfm_mep *mep;
372
373         ASSERT_RTNL();
374
375         mep = br_mep_find(br, instance);
376         if (!mep) {
377                 NL_SET_ERR_MSG_MOD(extack,
378                                    "MEP instance does not exists");
379                 return -ENOENT;
380         }
381
382         /* Check for no change in configuration */
383         if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
384                 return 0;
385
386         mep->cc_config = *config;
387         mep->ccm_tx_snumber = 1;
388
389         return 0;
390 }
391
392 int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
393                            u32 mepid,
394                            struct netlink_ext_ack *extack)
395 {
396         struct br_cfm_peer_mep *peer_mep;
397         struct br_cfm_mep *mep;
398
399         ASSERT_RTNL();
400
401         mep = br_mep_find(br, instance);
402         if (!mep) {
403                 NL_SET_ERR_MSG_MOD(extack,
404                                    "MEP instance does not exists");
405                 return -ENOENT;
406         }
407
408         peer_mep = br_peer_mep_find(mep, mepid);
409         if (peer_mep) {
410                 NL_SET_ERR_MSG_MOD(extack,
411                                    "Peer MEP-ID already exists");
412                 return -EEXIST;
413         }
414
415         peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
416         if (!peer_mep)
417                 return -ENOMEM;
418
419         peer_mep->mepid = mepid;
420         peer_mep->mep = mep;
421
422         hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
423
424         return 0;
425 }
426
427 int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
428                               u32 mepid,
429                               struct netlink_ext_ack *extack)
430 {
431         struct br_cfm_peer_mep *peer_mep;
432         struct br_cfm_mep *mep;
433
434         ASSERT_RTNL();
435
436         mep = br_mep_find(br, instance);
437         if (!mep) {
438                 NL_SET_ERR_MSG_MOD(extack,
439                                    "MEP instance does not exists");
440                 return -ENOENT;
441         }
442
443         peer_mep = br_peer_mep_find(mep, mepid);
444         if (!peer_mep) {
445                 NL_SET_ERR_MSG_MOD(extack,
446                                    "Peer MEP-ID does not exists");
447                 return -ENOENT;
448         }
449
450         hlist_del_rcu(&peer_mep->head);
451         kfree_rcu(peer_mep, rcu);
452
453         return 0;
454 }
455
456 int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
457                       const bool rdi, struct netlink_ext_ack *extack)
458 {
459         struct br_cfm_mep *mep;
460
461         ASSERT_RTNL();
462
463         mep = br_mep_find(br, instance);
464         if (!mep) {
465                 NL_SET_ERR_MSG_MOD(extack,
466                                    "MEP instance does not exists");
467                 return -ENOENT;
468         }
469
470         mep->rdi = rdi;
471
472         return 0;
473 }
474
475 int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
476                      const struct br_cfm_cc_ccm_tx_info *const tx_info,
477                      struct netlink_ext_ack *extack)
478 {
479         struct br_cfm_mep *mep;
480
481         ASSERT_RTNL();
482
483         mep = br_mep_find(br, instance);
484         if (!mep) {
485                 NL_SET_ERR_MSG_MOD(extack,
486                                    "MEP instance does not exists");
487                 return -ENOENT;
488         }
489
490         if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
491                 /* No change in tx_info. */
492                 if (mep->cc_ccm_tx_info.period == 0)
493                         /* Transmission is not enabled - just return */
494                         return 0;
495
496                 /* Transmission is ongoing, the end time is recalculated */
497                 mep->ccm_tx_end = jiffies +
498                                   usecs_to_jiffies(tx_info->period * 1000000);
499                 return 0;
500         }
501
502         if (tx_info->period == 0 && mep->cc_ccm_tx_info.period == 0)
503                 /* Some change in info and transmission is not ongoing */
504                 goto save;
505
506         if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
507                 /* Some change in info and transmission is ongoing
508                  * The end time is recalculated
509                  */
510                 mep->ccm_tx_end = jiffies +
511                                   usecs_to_jiffies(tx_info->period * 1000000);
512
513                 goto save;
514         }
515
516         if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
517                 cancel_delayed_work_sync(&mep->ccm_tx_dwork);
518                 goto save;
519         }
520
521         /* Start delayed work to transmit CCM frames. It is done with zero delay
522          * to send first frame immediately
523          */
524         mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
525         queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
526
527 save:
528         mep->cc_ccm_tx_info = *tx_info;
529
530         return 0;
531 }
532
533 /* Deletes the CFM instances on a specific bridge port
534  */
535 void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
536 {
537         struct hlist_node *n_store;
538         struct br_cfm_mep *mep;
539
540         ASSERT_RTNL();
541
542         hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head)
543                 if (mep->create.ifindex == port->dev->ifindex)
544                         mep_delete_implementation(br, mep);
545 }