static int bridge_table_lock(struct vr_interface *vif, uint8_t *mac) { uint8_t lock = 1; uint32_t hash; unsigned long t1s, t1ns, t2s, t2ns, diff; if (!vif->vif_bridge_table_lock) return -EINVAL; hash = vr_hash(mac, VR_ETHER_ALEN, 0); hash %= vr_num_cpus; vr_get_mono_time(&t1s, &t1ns); while (lock) { lock = __sync_lock_test_and_set(&vif->vif_bridge_table_lock[hash], lock); if (lock) { vr_get_mono_time(&t2s, &t2ns); if (t2ns >= t1ns) { diff = t2ns - t1ns; } else { diff = 999999999 - t1ns + t2ns; } if (diff >= 50000) { return -EINVAL; } } } return hash; }
unsigned int vr_assembler_table_scan(struct vr_fragment **head) { unsigned int scanned = 0; unsigned long sec, nsec, dest; struct vr_fragment *frag = *head, *next, **prev; prev = head; while (frag) { next = frag->f_next; vr_get_mono_time(&sec, &nsec); dest = frag->f_time + VR_ASSEMBLER_TIMEOUT_TIME; if (dest < frag->f_time) { if ((sec < frag->f_time) && (dest < sec)) { fragment_unlink_frag(prev, frag); fragment_free_frag(frag); } else { prev = &frag->f_next; } } else { if ((sec > dest) || (sec < frag->f_time)) { fragment_unlink_frag(prev, frag); fragment_free_frag(frag); } else { prev = &frag->f_next; } } scanned++; frag = next; } return scanned; }
static inline void fragment_entry_set(struct vr_fragment *fe, unsigned short vrf, struct vr_ip *iph, unsigned short sport, unsigned short dport) { unsigned long sec, nsec; fe->f_sip = iph->ip_saddr; fe->f_dip = iph->ip_daddr; fe->f_id = iph->ip_id; fe->f_vrf = vrf; fe->f_sport = sport; fe->f_dport = dport; vr_get_mono_time(&sec, &nsec); fe->f_time = sec; fe->f_expected = 0; fe->f_received = 0; return; }
static void fragment_reap(struct vr_btable *table, int start, unsigned int num_entries) { unsigned int i; struct vr_fragment *fe; unsigned long sec, nsec; vr_get_mono_time(&sec, &nsec); for (i = 0; i < ENTRIES_PER_SCAN; i++) { fe = vr_btable_get(table, (start + i) % num_entries); if (fe && fe->f_dip) { if (sec > fe->f_time + 1) vr_fragment_del(fe); } } return; }
struct vr_fragment * vr_fragment_get(struct vrouter *router, unsigned short vrf, struct vr_ip *iph) { unsigned int hash, index, i; struct vr_fragment_key key; struct vr_fragment *fe; unsigned long sec, nsec; fragment_key(&key, vrf, iph); hash = vr_hash(&key, sizeof(key), 0); index = (hash % FRAG_TABLE_ENTRIES) * FRAG_TABLE_BUCKETS; for (i = 0; i < FRAG_TABLE_BUCKETS; i++) { fe = fragment_entry_get(router, index + i); if (fe && !memcmp((const void *)&key, (const void *)&(fe->f_key), sizeof(key))) break; } if (i == FRAG_TABLE_BUCKETS) { index = (hash % FRAG_OTABLE_ENTRIES); for (i = 0; i < FRAG_OTABLE_ENTRIES; i++) { fe = fragment_oentry_get(router, (index + i) % FRAG_OTABLE_ENTRIES); if (fe && !memcmp((const void *)&key, (const void *)&(fe->f_key), sizeof(key))) break; } if (i == FRAG_OTABLE_ENTRIES) fe = NULL; } if (fe) { vr_get_mono_time(&sec, &nsec); fe->f_time = sec; } return fe; }
int vr_fragment_assembler(struct vr_fragment **head_p, struct vr_fragment_queue_element *vfqe) { int ret = 0; unsigned long sec, nsec; unsigned int list_length = 0, drop_reason; bool found = false, frag_head = false; struct vrouter *router; struct vr_ip *ip; struct vr_packet *pkt; struct vr_packet_node *pnode; struct vr_fragment *frag, *frag_flow, **prev = NULL; struct vr_fragment_queue_element *fqe; struct vr_fragment_key vfk; router = vfqe->fqe_router; pnode = &vfqe->fqe_pnode; pkt = pnode->pl_packet; ip = (struct vr_ip *)pkt_network_header(pkt); if (pnode->pl_flags & PN_FLAG_FRAGMENT_HEAD) frag_head = true; __fragment_key(&vfk, pnode->pl_vrf, pnode->pl_inner_src_ip, pnode->pl_inner_dst_ip, ip->ip_id); frag = *head_p; prev = head_p; while (frag) { list_length++; if (!memcmp(&frag->f_key, &vfk, sizeof(vfk))) { found = true; break; } prev = &frag->f_next; frag = frag->f_next; } if (!frag_head) { frag_flow = vr_fragment_get(router, pnode->pl_vrf, ip); if (frag_flow) { vr_fragment_flush_queue_element(vfqe); return 0; } } if (!found) { if (frag_head) { drop_reason = VP_DROP_CLONED_ORIGINAL; goto exit_assembly; } if (list_length > VR_MAX_FRAGMENTS_PER_ASSEMBLER_QUEUE) { drop_reason = VP_DROP_FRAGMENT_QUEUE_FAIL; goto exit_assembly; } frag = vr_zalloc(sizeof(*frag), VR_FRAGMENT_OBJECT); if (!frag) { ret = -ENOMEM; drop_reason = VP_DROP_NO_MEMORY; goto exit_assembly; } memcpy(&frag->f_key, &vfk, sizeof(vfk)); frag->f_port_info_valid = false; } vr_get_mono_time(&sec, &nsec); frag->f_time = sec; if (!found) { prev = head_p; frag->f_next = *head_p; *head_p = frag; } if (!frag_head) { vfqe->fqe_next = NULL; fqe = frag->f_qe; if (!fqe) { frag->f_qe = vfqe; } else { while (fqe) { if (fqe->fqe_next) { fqe = fqe->fqe_next; } else { break; } } fqe->fqe_next = vfqe; } } else { frag->f_port_info_valid = true; vr_fragment_queue_element_free(vfqe, VP_DROP_CLONED_ORIGINAL); } if (frag->f_port_info_valid) { while ((fqe = frag->f_qe)) { frag->f_qe = fqe->fqe_next; vr_fragment_flush_queue_element(fqe); } fragment_unlink_frag(prev, frag); fragment_free_frag(frag); } return 0; exit_assembly: vr_fragment_queue_element_free(vfqe, drop_reason); return ret; }