linux/net/bridge/br_cfm.c
<<
>>
Prefs
   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
   7static 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
  18static 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
  31static 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
  44static 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. */
  57static 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. */
  81static 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/* Convert the CCM PDU value to interval on interface. */
 105static u32 pdu_to_interval(u32 value)
 106{
 107        switch (value) {
 108        case 0:
 109                return BR_CFM_CCM_INTERVAL_NONE;
 110        case 1:
 111                return BR_CFM_CCM_INTERVAL_3_3_MS;
 112        case 2:
 113                return BR_CFM_CCM_INTERVAL_10_MS;
 114        case 3:
 115                return BR_CFM_CCM_INTERVAL_100_MS;
 116        case 4:
 117                return BR_CFM_CCM_INTERVAL_1_SEC;
 118        case 5:
 119                return BR_CFM_CCM_INTERVAL_10_SEC;
 120        case 6:
 121                return BR_CFM_CCM_INTERVAL_1_MIN;
 122        case 7:
 123                return BR_CFM_CCM_INTERVAL_10_MIN;
 124        }
 125        return BR_CFM_CCM_INTERVAL_NONE;
 126}
 127
 128static void ccm_rx_timer_start(struct br_cfm_peer_mep *peer_mep)
 129{
 130        u32 interval_us;
 131
 132        interval_us = interval_to_us(peer_mep->mep->cc_config.exp_interval);
 133        /* Function ccm_rx_dwork must be called with 1/4
 134         * of the configured CC 'expected_interval'
 135         * in order to detect CCM defect after 3.25 interval.
 136         */
 137        queue_delayed_work(system_wq, &peer_mep->ccm_rx_dwork,
 138                           usecs_to_jiffies(interval_us / 4));
 139}
 140
 141static void br_cfm_notify(int event, const struct net_bridge_port *port)
 142{
 143        u32 filter = RTEXT_FILTER_CFM_STATUS;
 144
 145        br_info_notify(event, port->br, NULL, filter);
 146}
 147
 148static void cc_peer_enable(struct br_cfm_peer_mep *peer_mep)
 149{
 150        memset(&peer_mep->cc_status, 0, sizeof(peer_mep->cc_status));
 151        peer_mep->ccm_rx_count_miss = 0;
 152
 153        ccm_rx_timer_start(peer_mep);
 154}
 155
 156static void cc_peer_disable(struct br_cfm_peer_mep *peer_mep)
 157{
 158        cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
 159}
 160
 161static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
 162                                       const struct br_cfm_cc_ccm_tx_info *const tx_info)
 163
 164{
 165        struct br_cfm_common_hdr *common_hdr;
 166        struct net_bridge_port *b_port;
 167        struct br_cfm_maid *maid;
 168        u8 *itu_reserved, *e_tlv;
 169        struct ethhdr *eth_hdr;
 170        struct sk_buff *skb;
 171        __be32 *status_tlv;
 172        __be32 *snumber;
 173        __be16 *mepid;
 174
 175        skb = dev_alloc_skb(CFM_CCM_MAX_FRAME_LENGTH);
 176        if (!skb)
 177                return NULL;
 178
 179        rcu_read_lock();
 180        b_port = rcu_dereference(mep->b_port);
 181        if (!b_port) {
 182                kfree_skb(skb);
 183                rcu_read_unlock();
 184                return NULL;
 185        }
 186        skb->dev = b_port->dev;
 187        rcu_read_unlock();
 188        /* The device cannot be deleted until the work_queue functions has
 189         * completed. This function is called from ccm_tx_work_expired()
 190         * that is a work_queue functions.
 191         */
 192
 193        skb->protocol = htons(ETH_P_CFM);
 194        skb->priority = CFM_FRAME_PRIO;
 195
 196        /* Ethernet header */
 197        eth_hdr = skb_put(skb, sizeof(*eth_hdr));
 198        ether_addr_copy(eth_hdr->h_dest, tx_info->dmac.addr);
 199        ether_addr_copy(eth_hdr->h_source, mep->config.unicast_mac.addr);
 200        eth_hdr->h_proto = htons(ETH_P_CFM);
 201
 202        /* Common CFM Header */
 203        common_hdr = skb_put(skb, sizeof(*common_hdr));
 204        common_hdr->mdlevel_version = mep->config.mdlevel << 5;
 205        common_hdr->opcode = BR_CFM_OPCODE_CCM;
 206        common_hdr->flags = (mep->rdi << 7) |
 207                            interval_to_pdu(mep->cc_config.exp_interval);
 208        common_hdr->tlv_offset = CFM_CCM_TLV_OFFSET;
 209
 210        /* Sequence number */
 211        snumber = skb_put(skb, sizeof(*snumber));
 212        if (tx_info->seq_no_update) {
 213                *snumber = cpu_to_be32(mep->ccm_tx_snumber);
 214                mep->ccm_tx_snumber += 1;
 215        } else {
 216                *snumber = 0;
 217        }
 218
 219        mepid = skb_put(skb, sizeof(*mepid));
 220        *mepid = cpu_to_be16((u16)mep->config.mepid);
 221
 222        maid = skb_put(skb, sizeof(*maid));
 223        memcpy(maid->data, mep->cc_config.exp_maid.data, sizeof(maid->data));
 224
 225        /* ITU reserved (CFM_CCM_ITU_RESERVED_SIZE octets) */
 226        itu_reserved = skb_put(skb, CFM_CCM_ITU_RESERVED_SIZE);
 227        memset(itu_reserved, 0, CFM_CCM_ITU_RESERVED_SIZE);
 228
 229        /* Generel CFM TLV format:
 230         * TLV type:            one byte
 231         * TLV value length:    two bytes
 232         * TLV value:           'TLV value length' bytes
 233         */
 234
 235        /* Port status TLV. The value length is 1. Total of 4 bytes. */
 236        if (tx_info->port_tlv) {
 237                status_tlv = skb_put(skb, sizeof(*status_tlv));
 238                *status_tlv = cpu_to_be32((CFM_PORT_STATUS_TLV_TYPE << 24) |
 239                                          (1 << 8) |    /* Value length */
 240                                          (tx_info->port_tlv_value & 0xFF));
 241        }
 242
 243        /* Interface status TLV. The value length is 1. Total of 4 bytes. */
 244        if (tx_info->if_tlv) {
 245                status_tlv = skb_put(skb, sizeof(*status_tlv));
 246                *status_tlv = cpu_to_be32((CFM_IF_STATUS_TLV_TYPE << 24) |
 247                                          (1 << 8) |    /* Value length */
 248                                          (tx_info->if_tlv_value & 0xFF));
 249        }
 250
 251        /* End TLV */
 252        e_tlv = skb_put(skb, sizeof(*e_tlv));
 253        *e_tlv = CFM_ENDE_TLV_TYPE;
 254
 255        return skb;
 256}
 257
 258static void ccm_frame_tx(struct sk_buff *skb)
 259{
 260        skb_reset_network_header(skb);
 261        dev_queue_xmit(skb);
 262}
 263
 264/* This function is called with the configured CC 'expected_interval'
 265 * in order to drive CCM transmission when enabled.
 266 */
 267static void ccm_tx_work_expired(struct work_struct *work)
 268{
 269        struct delayed_work *del_work;
 270        struct br_cfm_mep *mep;
 271        struct sk_buff *skb;
 272        u32 interval_us;
 273
 274        del_work = to_delayed_work(work);
 275        mep = container_of(del_work, struct br_cfm_mep, ccm_tx_dwork);
 276
 277        if (time_before_eq(mep->ccm_tx_end, jiffies)) {
 278                /* Transmission period has ended */
 279                mep->cc_ccm_tx_info.period = 0;
 280                return;
 281        }
 282
 283        skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
 284        if (skb)
 285                ccm_frame_tx(skb);
 286
 287        interval_us = interval_to_us(mep->cc_config.exp_interval);
 288        queue_delayed_work(system_wq, &mep->ccm_tx_dwork,
 289                           usecs_to_jiffies(interval_us));
 290}
 291
 292/* This function is called with 1/4 of the configured CC 'expected_interval'
 293 * in order to detect CCM defect after 3.25 interval.
 294 */
 295static void ccm_rx_work_expired(struct work_struct *work)
 296{
 297        struct br_cfm_peer_mep *peer_mep;
 298        struct net_bridge_port *b_port;
 299        struct delayed_work *del_work;
 300
 301        del_work = to_delayed_work(work);
 302        peer_mep = container_of(del_work, struct br_cfm_peer_mep, ccm_rx_dwork);
 303
 304        /* After 13 counts (4 * 3,25) then 3.25 intervals are expired */
 305        if (peer_mep->ccm_rx_count_miss < 13) {
 306                /* 3.25 intervals are NOT expired without CCM reception */
 307                peer_mep->ccm_rx_count_miss++;
 308
 309                /* Start timer again */
 310                ccm_rx_timer_start(peer_mep);
 311        } else {
 312                /* 3.25 intervals are expired without CCM reception.
 313                 * CCM defect detected
 314                 */
 315                peer_mep->cc_status.ccm_defect = true;
 316
 317                /* Change in CCM defect status - notify */
 318                rcu_read_lock();
 319                b_port = rcu_dereference(peer_mep->mep->b_port);
 320                if (b_port)
 321                        br_cfm_notify(RTM_NEWLINK, b_port);
 322                rcu_read_unlock();
 323        }
 324}
 325
 326static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index,
 327                           struct br_cfm_peer_mep *peer_mep)
 328{
 329        __be32 *s_tlv;
 330        __be32 _s_tlv;
 331        u32 h_s_tlv;
 332        u8 *e_tlv;
 333        u8 _e_tlv;
 334
 335        e_tlv = skb_header_pointer(skb, index, sizeof(_e_tlv), &_e_tlv);
 336        if (!e_tlv)
 337                return 0;
 338
 339        /* TLV is present - get the status TLV */
 340        s_tlv = skb_header_pointer(skb,
 341                                   index,
 342                                   sizeof(_s_tlv), &_s_tlv);
 343        if (!s_tlv)
 344                return 0;
 345
 346        h_s_tlv = ntohl(*s_tlv);
 347        if ((h_s_tlv >> 24) == CFM_IF_STATUS_TLV_TYPE) {
 348                /* Interface status TLV */
 349                peer_mep->cc_status.tlv_seen = true;
 350                peer_mep->cc_status.if_tlv_value = (h_s_tlv & 0xFF);
 351        }
 352
 353        if ((h_s_tlv >> 24) == CFM_PORT_STATUS_TLV_TYPE) {
 354                /* Port status TLV */
 355                peer_mep->cc_status.tlv_seen = true;
 356                peer_mep->cc_status.port_tlv_value = (h_s_tlv & 0xFF);
 357        }
 358
 359        /* The Sender ID TLV is not handled */
 360        /* The Organization-Specific TLV is not handled */
 361
 362        /* Return the length of this tlv.
 363         * This is the length of the value field plus 3 bytes for size of type
 364         * field and length field
 365         */
 366        return ((h_s_tlv >> 8) & 0xFFFF) + 3;
 367}
 368
 369/* note: already called with rcu_read_lock */
 370static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
 371{
 372        u32 mdlevel, interval, size, index, max;
 373        const struct br_cfm_common_hdr *hdr;
 374        struct br_cfm_peer_mep *peer_mep;
 375        const struct br_cfm_maid *maid;
 376        struct br_cfm_common_hdr _hdr;
 377        struct br_cfm_maid _maid;
 378        struct br_cfm_mep *mep;
 379        struct net_bridge *br;
 380        __be32 *snumber;
 381        __be32 _snumber;
 382        __be16 *mepid;
 383        __be16 _mepid;
 384
 385        if (port->state == BR_STATE_DISABLED)
 386                return 0;
 387
 388        hdr = skb_header_pointer(skb, 0, sizeof(_hdr), &_hdr);
 389        if (!hdr)
 390                return 1;
 391
 392        br = port->br;
 393        mep = br_mep_find_ifindex(br, port->dev->ifindex);
 394        if (unlikely(!mep))
 395                /* No MEP on this port - must be forwarded */
 396                return 0;
 397
 398        mdlevel = hdr->mdlevel_version >> 5;
 399        if (mdlevel > mep->config.mdlevel)
 400                /* The level is above this MEP level - must be forwarded */
 401                return 0;
 402
 403        if ((hdr->mdlevel_version & 0x1F) != 0) {
 404                /* Invalid version */
 405                mep->status.version_unexp_seen = true;
 406                return 1;
 407        }
 408
 409        if (mdlevel < mep->config.mdlevel) {
 410                /* The level is below this MEP level */
 411                mep->status.rx_level_low_seen = true;
 412                return 1;
 413        }
 414
 415        if (hdr->opcode == BR_CFM_OPCODE_CCM) {
 416                /* CCM PDU received. */
 417                /* MA ID is after common header + sequence number + MEP ID */
 418                maid = skb_header_pointer(skb,
 419                                          CFM_CCM_PDU_MAID_OFFSET,
 420                                          sizeof(_maid), &_maid);
 421                if (!maid)
 422                        return 1;
 423                if (memcmp(maid->data, mep->cc_config.exp_maid.data,
 424                           sizeof(maid->data)))
 425                        /* MA ID not as expected */
 426                        return 1;
 427
 428                /* MEP ID is after common header + sequence number */
 429                mepid = skb_header_pointer(skb,
 430                                           CFM_CCM_PDU_MEPID_OFFSET,
 431                                           sizeof(_mepid), &_mepid);
 432                if (!mepid)
 433                        return 1;
 434                peer_mep = br_peer_mep_find(mep, (u32)ntohs(*mepid));
 435                if (!peer_mep)
 436                        return 1;
 437
 438                /* Interval is in common header flags */
 439                interval = hdr->flags & 0x07;
 440                if (mep->cc_config.exp_interval != pdu_to_interval(interval))
 441                        /* Interval not as expected */
 442                        return 1;
 443
 444                /* A valid CCM frame is received */
 445                if (peer_mep->cc_status.ccm_defect) {
 446                        peer_mep->cc_status.ccm_defect = false;
 447
 448                        /* Change in CCM defect status - notify */
 449                        br_cfm_notify(RTM_NEWLINK, port);
 450
 451                        /* Start CCM RX timer */
 452                        ccm_rx_timer_start(peer_mep);
 453                }
 454
 455                peer_mep->cc_status.seen = true;
 456                peer_mep->ccm_rx_count_miss = 0;
 457
 458                /* RDI is in common header flags */
 459                peer_mep->cc_status.rdi = (hdr->flags & 0x80) ? true : false;
 460
 461                /* Sequence number is after common header */
 462                snumber = skb_header_pointer(skb,
 463                                             CFM_CCM_PDU_SEQNR_OFFSET,
 464                                             sizeof(_snumber), &_snumber);
 465                if (!snumber)
 466                        return 1;
 467                if (ntohl(*snumber) != (mep->ccm_rx_snumber + 1))
 468                        /* Unexpected sequence number */
 469                        peer_mep->cc_status.seq_unexp_seen = true;
 470
 471                mep->ccm_rx_snumber = ntohl(*snumber);
 472
 473                /* TLV end is after common header + sequence number + MEP ID +
 474                 * MA ID + ITU reserved
 475                 */
 476                index = CFM_CCM_PDU_TLV_OFFSET;
 477                max = 0;
 478                do { /* Handle all TLVs */
 479                        size = ccm_tlv_extract(skb, index, peer_mep);
 480                        index += size;
 481                        max += 1;
 482                } while (size != 0 && max < 4); /* Max four TLVs possible */
 483
 484                return 1;
 485        }
 486
 487        mep->status.opcode_unexp_seen = true;
 488
 489        return 1;
 490}
 491
 492static struct br_frame_type cfm_frame_type __read_mostly = {
 493        .type = cpu_to_be16(ETH_P_CFM),
 494        .frame_handler = br_cfm_frame_rx,
 495};
 496
 497int br_cfm_mep_create(struct net_bridge *br,
 498                      const u32 instance,
 499                      struct br_cfm_mep_create *const create,
 500                      struct netlink_ext_ack *extack)
 501{
 502        struct net_bridge_port *p;
 503        struct br_cfm_mep *mep;
 504
 505        ASSERT_RTNL();
 506
 507        if (create->domain == BR_CFM_VLAN) {
 508                NL_SET_ERR_MSG_MOD(extack,
 509                                   "VLAN domain not supported");
 510                return -EINVAL;
 511        }
 512        if (create->domain != BR_CFM_PORT) {
 513                NL_SET_ERR_MSG_MOD(extack,
 514                                   "Invalid domain value");
 515                return -EINVAL;
 516        }
 517        if (create->direction == BR_CFM_MEP_DIRECTION_UP) {
 518                NL_SET_ERR_MSG_MOD(extack,
 519                                   "Up-MEP not supported");
 520                return -EINVAL;
 521        }
 522        if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) {
 523                NL_SET_ERR_MSG_MOD(extack,
 524                                   "Invalid direction value");
 525                return -EINVAL;
 526        }
 527        p = br_mep_get_port(br, create->ifindex);
 528        if (!p) {
 529                NL_SET_ERR_MSG_MOD(extack,
 530                                   "Port is not related to bridge");
 531                return -EINVAL;
 532        }
 533        mep = br_mep_find(br, instance);
 534        if (mep) {
 535                NL_SET_ERR_MSG_MOD(extack,
 536                                   "MEP instance already exists");
 537                return -EEXIST;
 538        }
 539
 540        /* In PORT domain only one instance can be created per port */
 541        if (create->domain == BR_CFM_PORT) {
 542                mep = br_mep_find_ifindex(br, create->ifindex);
 543                if (mep) {
 544                        NL_SET_ERR_MSG_MOD(extack,
 545                                           "Only one Port MEP on a port allowed");
 546                        return -EINVAL;
 547                }
 548        }
 549
 550        mep = kzalloc(sizeof(*mep), GFP_KERNEL);
 551        if (!mep)
 552                return -ENOMEM;
 553
 554        mep->create = *create;
 555        mep->instance = instance;
 556        rcu_assign_pointer(mep->b_port, p);
 557
 558        INIT_HLIST_HEAD(&mep->peer_mep_list);
 559        INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
 560
 561        if (hlist_empty(&br->mep_list))
 562                br_add_frame(br, &cfm_frame_type);
 563
 564        hlist_add_tail_rcu(&mep->head, &br->mep_list);
 565
 566        return 0;
 567}
 568
 569static void mep_delete_implementation(struct net_bridge *br,
 570                                      struct br_cfm_mep *mep)
 571{
 572        struct br_cfm_peer_mep *peer_mep;
 573        struct hlist_node *n_store;
 574
 575        ASSERT_RTNL();
 576
 577        /* Empty and free peer MEP list */
 578        hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) {
 579                cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
 580                hlist_del_rcu(&peer_mep->head);
 581                kfree_rcu(peer_mep, rcu);
 582        }
 583
 584        cancel_delayed_work_sync(&mep->ccm_tx_dwork);
 585
 586        RCU_INIT_POINTER(mep->b_port, NULL);
 587        hlist_del_rcu(&mep->head);
 588        kfree_rcu(mep, rcu);
 589
 590        if (hlist_empty(&br->mep_list))
 591                br_del_frame(br, &cfm_frame_type);
 592}
 593
 594int br_cfm_mep_delete(struct net_bridge *br,
 595                      const u32 instance,
 596                      struct netlink_ext_ack *extack)
 597{
 598        struct br_cfm_mep *mep;
 599
 600        ASSERT_RTNL();
 601
 602        mep = br_mep_find(br, instance);
 603        if (!mep) {
 604                NL_SET_ERR_MSG_MOD(extack,
 605                                   "MEP instance does not exists");
 606                return -ENOENT;
 607        }
 608
 609        mep_delete_implementation(br, mep);
 610
 611        return 0;
 612}
 613
 614int br_cfm_mep_config_set(struct net_bridge *br,
 615                          const u32 instance,
 616                          const struct br_cfm_mep_config *const config,
 617                          struct netlink_ext_ack *extack)
 618{
 619        struct br_cfm_mep *mep;
 620
 621        ASSERT_RTNL();
 622
 623        mep = br_mep_find(br, instance);
 624        if (!mep) {
 625                NL_SET_ERR_MSG_MOD(extack,
 626                                   "MEP instance does not exists");
 627                return -ENOENT;
 628        }
 629
 630        mep->config = *config;
 631
 632        return 0;
 633}
 634
 635int br_cfm_cc_config_set(struct net_bridge *br,
 636                         const u32 instance,
 637                         const struct br_cfm_cc_config *const config,
 638                         struct netlink_ext_ack *extack)
 639{
 640        struct br_cfm_peer_mep *peer_mep;
 641        struct br_cfm_mep *mep;
 642
 643        ASSERT_RTNL();
 644
 645        mep = br_mep_find(br, instance);
 646        if (!mep) {
 647                NL_SET_ERR_MSG_MOD(extack,
 648                                   "MEP instance does not exists");
 649                return -ENOENT;
 650        }
 651
 652        /* Check for no change in configuration */
 653        if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
 654                return 0;
 655
 656        if (config->enable && !mep->cc_config.enable)
 657                /* CC is enabled */
 658                hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
 659                        cc_peer_enable(peer_mep);
 660
 661        if (!config->enable && mep->cc_config.enable)
 662                /* CC is disabled */
 663                hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
 664                        cc_peer_disable(peer_mep);
 665
 666        mep->cc_config = *config;
 667        mep->ccm_rx_snumber = 0;
 668        mep->ccm_tx_snumber = 1;
 669
 670        return 0;
 671}
 672
 673int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 674                           u32 mepid,
 675                           struct netlink_ext_ack *extack)
 676{
 677        struct br_cfm_peer_mep *peer_mep;
 678        struct br_cfm_mep *mep;
 679
 680        ASSERT_RTNL();
 681
 682        mep = br_mep_find(br, instance);
 683        if (!mep) {
 684                NL_SET_ERR_MSG_MOD(extack,
 685                                   "MEP instance does not exists");
 686                return -ENOENT;
 687        }
 688
 689        peer_mep = br_peer_mep_find(mep, mepid);
 690        if (peer_mep) {
 691                NL_SET_ERR_MSG_MOD(extack,
 692                                   "Peer MEP-ID already exists");
 693                return -EEXIST;
 694        }
 695
 696        peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
 697        if (!peer_mep)
 698                return -ENOMEM;
 699
 700        peer_mep->mepid = mepid;
 701        peer_mep->mep = mep;
 702        INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);
 703
 704        if (mep->cc_config.enable)
 705                cc_peer_enable(peer_mep);
 706
 707        hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
 708
 709        return 0;
 710}
 711
 712int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 713                              u32 mepid,
 714                              struct netlink_ext_ack *extack)
 715{
 716        struct br_cfm_peer_mep *peer_mep;
 717        struct br_cfm_mep *mep;
 718
 719        ASSERT_RTNL();
 720
 721        mep = br_mep_find(br, instance);
 722        if (!mep) {
 723                NL_SET_ERR_MSG_MOD(extack,
 724                                   "MEP instance does not exists");
 725                return -ENOENT;
 726        }
 727
 728        peer_mep = br_peer_mep_find(mep, mepid);
 729        if (!peer_mep) {
 730                NL_SET_ERR_MSG_MOD(extack,
 731                                   "Peer MEP-ID does not exists");
 732                return -ENOENT;
 733        }
 734
 735        cc_peer_disable(peer_mep);
 736
 737        hlist_del_rcu(&peer_mep->head);
 738        kfree_rcu(peer_mep, rcu);
 739
 740        return 0;
 741}
 742
 743int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 744                      const bool rdi, struct netlink_ext_ack *extack)
 745{
 746        struct br_cfm_mep *mep;
 747
 748        ASSERT_RTNL();
 749
 750        mep = br_mep_find(br, instance);
 751        if (!mep) {
 752                NL_SET_ERR_MSG_MOD(extack,
 753                                   "MEP instance does not exists");
 754                return -ENOENT;
 755        }
 756
 757        mep->rdi = rdi;
 758
 759        return 0;
 760}
 761
 762int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 763                     const struct br_cfm_cc_ccm_tx_info *const tx_info,
 764                     struct netlink_ext_ack *extack)
 765{
 766        struct br_cfm_mep *mep;
 767
 768        ASSERT_RTNL();
 769
 770        mep = br_mep_find(br, instance);
 771        if (!mep) {
 772                NL_SET_ERR_MSG_MOD(extack,
 773                                   "MEP instance does not exists");
 774                return -ENOENT;
 775        }
 776
 777        if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
 778                /* No change in tx_info. */
 779                if (mep->cc_ccm_tx_info.period == 0)
 780                        /* Transmission is not enabled - just return */
 781                        return 0;
 782
 783                /* Transmission is ongoing, the end time is recalculated */
 784                mep->ccm_tx_end = jiffies +
 785                                  usecs_to_jiffies(tx_info->period * 1000000);
 786                return 0;
 787        }
 788
 789        if (tx_info->period == 0 && mep->cc_ccm_tx_info.period == 0)
 790                /* Some change in info and transmission is not ongoing */
 791                goto save;
 792
 793        if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
 794                /* Some change in info and transmission is ongoing
 795                 * The end time is recalculated
 796                 */
 797                mep->ccm_tx_end = jiffies +
 798                                  usecs_to_jiffies(tx_info->period * 1000000);
 799
 800                goto save;
 801        }
 802
 803        if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
 804                cancel_delayed_work_sync(&mep->ccm_tx_dwork);
 805                goto save;
 806        }
 807
 808        /* Start delayed work to transmit CCM frames. It is done with zero delay
 809         * to send first frame immediately
 810         */
 811        mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
 812        queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
 813
 814save:
 815        mep->cc_ccm_tx_info = *tx_info;
 816
 817        return 0;
 818}
 819
 820int br_cfm_mep_count(struct net_bridge *br, u32 *count)
 821{
 822        struct br_cfm_mep *mep;
 823
 824        *count = 0;
 825
 826        rcu_read_lock();
 827        hlist_for_each_entry_rcu(mep, &br->mep_list, head)
 828                *count += 1;
 829        rcu_read_unlock();
 830
 831        return 0;
 832}
 833
 834int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count)
 835{
 836        struct br_cfm_peer_mep *peer_mep;
 837        struct br_cfm_mep *mep;
 838
 839        *count = 0;
 840
 841        rcu_read_lock();
 842        hlist_for_each_entry_rcu(mep, &br->mep_list, head)
 843                hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head)
 844                        *count += 1;
 845        rcu_read_unlock();
 846
 847        return 0;
 848}
 849
 850bool br_cfm_created(struct net_bridge *br)
 851{
 852        return !hlist_empty(&br->mep_list);
 853}
 854
 855/* Deletes the CFM instances on a specific bridge port
 856 */
 857void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
 858{
 859        struct hlist_node *n_store;
 860        struct br_cfm_mep *mep;
 861
 862        ASSERT_RTNL();
 863
 864        hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head)
 865                if (mep->create.ifindex == port->dev->ifindex)
 866                        mep_delete_implementation(br, mep);
 867}
 868