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; }
unsigned int vr_flow_inet_input(struct vrouter *router, unsigned short vrf, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { struct vr_flow_key key, *key_p = &key; struct vr_ip *ip, *icmp_pl_ip = NULL; struct vr_fragment *frag; unsigned int flow_parse_res; unsigned int trap_res = 0; unsigned int sip, dip; unsigned short *t_hdr, sport, dport; unsigned char ip_proto; struct vr_icmp *icmph; /* * interface is in a mode where it wants all packets to be received * without doing lookups to figure out whether packets were destined * to me or not */ if (pkt->vp_flags & VP_FLAG_TO_ME) return vr_ip_rcv(router, pkt, fmd); ip = (struct vr_ip *)pkt_network_header(pkt); ip_proto = ip->ip_proto; /* if the packet is not a fragment, we easily know the sport, and dport */ if (vr_ip_transport_header_valid(ip)) { t_hdr = (unsigned short *)((char *)ip + (ip->ip_hl * 4)); if (ip_proto == VR_IP_PROTO_ICMP) { icmph = (struct vr_icmp *)t_hdr; if (vr_icmp_error(icmph)) { icmp_pl_ip = (struct vr_ip *)(icmph + 1); ip_proto = icmp_pl_ip->ip_proto; t_hdr = (unsigned short *)((char *)icmp_pl_ip + (icmp_pl_ip->ip_hl * 4)); if (ip_proto == VR_IP_PROTO_ICMP) icmph = (struct vr_icmp *)t_hdr; } } if (ip_proto == VR_IP_PROTO_ICMP) { if (icmph->icmp_type == VR_ICMP_TYPE_ECHO || icmph->icmp_type == VR_ICMP_TYPE_ECHO_REPLY) { sport = icmph->icmp_eid; dport = VR_ICMP_TYPE_ECHO_REPLY; } else { sport = 0; dport = icmph->icmp_type; } } else { if (icmp_pl_ip) { sport = *(t_hdr + 1); dport = *t_hdr; } else { sport = *t_hdr; dport = *(t_hdr + 1); } } } else { /* ...else, we need to get it from somewhere */ flow_parse_res = vr_flow_parse(router, NULL, pkt, &trap_res); /* ...and it really matters only if we need to do a flow lookup */ if (flow_parse_res == VR_FLOW_LOOKUP) { frag = vr_fragment_get(router, vrf, ip); if (!frag) { vr_pfree(pkt, VP_DROP_FRAGMENTS); return 0; } sport = frag->f_sport; dport = frag->f_dport; if (vr_ip_fragment_tail(ip)) vr_fragment_del(frag); } else { /* * since there is no other way of deriving a key, set the * key_p to NULL, indicating to code below that there is * indeed no need for flow lookup */ key_p = NULL; } } if (key_p) { /* we have everything to make a key */ if (icmp_pl_ip) { sip = icmp_pl_ip->ip_daddr; dip = icmp_pl_ip->ip_saddr; } else { sip = ip->ip_saddr; dip = ip->ip_daddr; } vr_get_flow_key(key_p, fmd->fmd_vlan, pkt, sip, dip, ip_proto, sport, dport); flow_parse_res = vr_flow_parse(router, key_p, pkt, &trap_res); if (flow_parse_res == VR_FLOW_LOOKUP && vr_ip_fragment_head(ip)) vr_fragment_add(router, vrf, ip, key_p->key_src_port, key_p->key_dst_port); if (flow_parse_res == VR_FLOW_BYPASS) { return vr_flow_forward(vrf, pkt, proto, fmd); } else if (flow_parse_res == VR_FLOW_TRAP) { return vr_trap(pkt, vrf, trap_res, NULL); } return vr_flow_lookup(router, vrf, key_p, pkt, proto, fmd); } /* * ...come here, when there is not enough information to do a * flow lookup */ return vr_flow_forward(vrf, pkt, proto, fmd); }