void handle_arpreq(struct sr_instance *sr, struct sr_arpreq *req) { time_t now = time(NULL); if (difftime(now, req->sent) >= 0.9) { if (req->times_sent >= 5) { struct sr_packet *pkt_pending = req->packets; struct sr_if *interface = sr_get_interface(sr, pkt_pending->iface); while (pkt_pending) { icmp_handler(sr, pkt_pending->buf, 0, interface->ip, HOST_UNREACHABLE); pkt_pending = pkt_pending->next; } sr_arpreq_destroy(&(sr->cache), req); } else { int packet_len = sizeof(sr_ethernet_hdr_t) + sizeof(sr_arp_hdr_t); uint8_t *pkt = malloc(packet_len); struct sr_ethernet_hdr *eth_hdr = (struct sr_ethernet_hdr *)pkt; struct sr_if *interface = sr_get_interface(sr, req->packets->iface); uint8_t hrd_addr[ETHER_ADDR_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; enum sr_ethertype eth_arp = ethertype_arp; enum sr_ethertype eth_ip = ethertype_ip; memcpy(eth_hdr->ether_dhost, hrd_addr, ETHER_ADDR_LEN); memcpy(eth_hdr->ether_shost, interface->addr, ETHER_ADDR_LEN); eth_hdr->ether_type = htons(eth_arp); struct sr_arp_hdr *arp_hdr = (struct sr_arp_hdr *)(sizeof(sr_ethernet_hdr_t) + pkt); enum sr_arp_hrd_fmt hrd_eth = arp_hrd_ethernet; enum sr_arp_opcode arp_op_req = arp_op_request; arp_hdr->ar_hrd = htons(hrd_eth); arp_hdr->ar_pro = htons(eth_ip); arp_hdr->ar_hln = ETHER_ADDR_LEN; arp_hdr->ar_pln = 4; arp_hdr->ar_op = htons(arp_op_req); arp_hdr->ar_sip = interface->ip; arp_hdr->ar_tip = req->ip; memcpy(arp_hdr->ar_sha, interface->addr, ETHER_ADDR_LEN); memcpy(arp_hdr->ar_tha, hrd_addr, ETHER_ADDR_LEN); printf("Sending Packets.. [3].\n"); sr_send_packet(sr, pkt, packet_len, interface->name); free(pkt); req->sent = now; req->times_sent++; } } }
/* * @brief Handles ip packets * @param skb Pointer to the skb, whose data pointer must be set to ip header. * @return 0 on success. -1 on failure. The packet is freed in both cases */ int ip_handler(struct sk_buff *skb) { int ret = -1; struct ip_hdr *iph; struct pal_dip *dip; if (!skb_ip_csum_ok(skb)) { PAL_DEBUG("skb ip csum error\n"); pal_cur_thread_conf()->stats.ip.csum_err++; goto free_out; } iph = skb_ip_header(skb); if ((unsigned)iph->ihl < 5 || skb_len(skb) < (unsigned)iph->ihl * 4) { pal_cur_thread_conf()->stats.ip.trunc_pkts++; goto free_out; } dip = pal_ipg_find_ip(iph->daddr); if (dip == NULL) { pal_cur_thread_conf()->stats.ip.unknown_dst++; /* TODO: use default ip group. */ PAL_DEBUG("dip "NIPQUAD_FMT" not found\n", NIPQUAD(iph->daddr)); goto free_out; } switch (dip->type) { case PAL_DIP_USER: skb_pull(skb, iph->ihl * 4); skb_reset_l4_header(skb); switch (iph->protocol) { case PAL_IPPROTO_TCP: ret = tcp_dispatch(skb, dip); break; case PAL_IPPROTO_UDP: ret = udp_dispatch(skb, dip); break; case PAL_IPPROTO_ICMP: if (dip->ipg->flags & PAL_IPG_F_HANDLEICMP) { ret = icmp_dispatch(skb, dip); } else { ret = icmp_handler(skb); } break; default: pal_cur_thread_conf()->stats.ip.unknown_proto_pkts++; pal_cur_thread_conf()->stats.ip.unknown_proto_bytes += skb_l2_len(skb); PAL_DEBUG("UNKNOWN IP PKTS\n"); if (dip->ipg->flags & PAL_IPG_F_HANDLEUNKNOWIPPROTO) { ret = dispatch_pkt(skb, dip); } else { ret = -1; } break; } if (ret >= 0) { pal_ipg_put_ip(dip->ip); return 0; } goto putdip_out; case PAL_DIP_VNIC: if (dip->port != skb->recv_if) { pal_cur_thread_conf()->stats.tap.port_err++; PAL_DEBUG("got a packet for wrong vnic\n"); goto putdip_out; } /* push the data pointer to include the ethernet header */ skb_push(skb, sizeof(struct eth_hdr)); if (pal_send_to_vnic(dip->port, skb) == 0) { pal_skb_free(skb); pal_ipg_put_ip(dip->ip); return 0; } goto putdip_out; default: pal_cur_thread_conf()->stats.ip.unknown_dst++; PAL_DEBUG("unknown\n"); goto putdip_out; } putdip_out: pal_ipg_put_ip(dip->ip); free_out: pal_skb_free(skb); return ret; }