/* Attempts to make 'ml' learn from the fact that a frame from 'src_mac' was * just observed arriving from 'src_port' on the given 'vlan'. * * Returns nonzero if we actually learned something from this, zero if it just * confirms what we already knew. The nonzero return value is the tag of flows * that now need revalidation. * * The 'vlan' parameter is used to maintain separate per-VLAN learning tables. * Specify 0 if this behavior is undesirable. * * 'lock_type' specifies whether the entry should be locked or existing locks * are check. */ tag_type mac_learning_learn(struct mac_learning *ml, const uint8_t src_mac[ETH_ADDR_LEN], uint16_t vlan, uint16_t src_port, enum grat_arp_lock_type lock_type) { struct mac_entry *e; struct list *bucket; if (!is_learning_vlan(ml, vlan)) { return 0; } if (eth_addr_is_multicast(src_mac)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 30); VLOG_DBG_RL(&rl, "multicast packet source "ETH_ADDR_FMT, ETH_ADDR_ARGS(src_mac)); return 0; } bucket = mac_table_bucket(ml, src_mac, vlan); e = search_bucket(bucket, src_mac, vlan); if (!e) { if (!list_is_empty(&ml->free)) { e = mac_entry_from_lru_node(ml->free.next); } else { e = mac_entry_from_lru_node(ml->lrus.next); list_remove(&e->hash_node); } memcpy(e->mac, src_mac, ETH_ADDR_LEN); list_push_front(bucket, &e->hash_node); e->port = -1; e->vlan = vlan; e->tag = make_unknown_mac_tag(ml, src_mac, vlan); e->grat_arp_lock = TIME_MIN; } if (lock_type != GRAT_ARP_LOCK_CHECK || time_now() >= e->grat_arp_lock) { /* Make the entry most-recently-used. */ list_remove(&e->lru_node); list_push_back(&ml->lrus, &e->lru_node); e->expires = time_now() + MAC_ENTRY_IDLE_TIME; if (lock_type == GRAT_ARP_LOCK_SET) { e->grat_arp_lock = time_now() + MAC_GRAT_ARP_LOCK_TIME; } /* Did we learn something? */ if (e->port != src_port) { tag_type old_tag = e->tag; e->port = src_port; e->tag = tag_create_random(); COVERAGE_INC(mac_learning_learned); return old_tag; } } return 0; }
static Block_t * get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size) { GFAllctr_t *gfallctr = (GFAllctr_t *) allctr; int unsafe_bi, min_bi; Block_t *blk; ASSERT(!cand_blk || cand_size >= size); unsafe_bi = BKT_IX(gfallctr, size); min_bi = find_bucket(&gfallctr->bucket_mask, unsafe_bi); if (min_bi < 0) return NULL; if (min_bi == unsafe_bi) { blk = search_bucket(allctr, min_bi, size); if (blk) { if (cand_blk && cand_size <= MBC_FBLK_SZ(blk)) return NULL; /* cand_blk was better */ unlink_free_block(allctr, blk); return blk; } if (min_bi < NO_OF_BKTS - 1) { min_bi = find_bucket(&gfallctr->bucket_mask, min_bi + 1); if (min_bi < 0) return NULL; } else return NULL; } else { ASSERT(min_bi > unsafe_bi); } /* We are guaranteed to find a block that fits in this bucket */ blk = search_bucket(allctr, min_bi, size); ASSERT(blk); if (cand_blk && cand_size <= MBC_FBLK_SZ(blk)) return NULL; /* cand_blk was better */ unlink_free_block(allctr, blk); return blk; }
/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml'. Returns the port on which a * frame destined for 'dst' should be sent, -1 if unknown. * * Adds to '*tag' (which the caller must have initialized) the tag that should * be attached to any flow created based on the return value, if any, to allow * those flows to be revalidated when the MAC learning entry changes. * * 'is_grat_arp_locked' is an optional parameter that returns whether the entry * is currently locked.*/ int mac_learning_lookup_tag(const struct mac_learning *ml, const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan, tag_type *tag, bool *is_grat_arp_locked) { if (eth_addr_is_multicast(dst) || !is_learning_vlan(ml, vlan)) { return -1; } else { struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst, vlan), dst, vlan); if (e) { *tag |= e->tag; if (is_grat_arp_locked) { *is_grat_arp_locked = time_now() < e->grat_arp_lock; } return e->port; } else { *tag |= make_unknown_mac_tag(ml, dst, vlan); return -1; } } }