Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : #include <stdint.h> 5 : #include "base/os.h" 6 : #include <vr_defs.h> 7 : #include <cmn/agent_cmn.h> 8 : #include <oper/interface_common.h> 9 : #include <oper/vn.h> 10 : 11 : #include <pkt/pkt_init.h> 12 : #include <pkt/flow_table.h> 13 : #include <services/icmp_error_proto.h> 14 : #include <services/icmp_error_handler.h> 15 : 16 0 : IcmpErrorHandler::IcmpErrorHandler(Agent *agent, IcmpErrorProto *proto, 17 : boost::shared_ptr<PktInfo> info, 18 0 : boost::asio::io_context *io) : 19 0 : ProtoHandler(agent, info, *io), proto_(proto) { 20 0 : } 21 : 22 0 : IcmpErrorHandler::~IcmpErrorHandler() { 23 0 : } 24 : 25 0 : bool IcmpErrorHandler::ValidatePacket() { 26 0 : if (pkt_info_->len < (sizeof(struct ether_header) + sizeof(struct ip))) 27 0 : return false; 28 0 : return true; 29 : } 30 : 31 0 : bool IcmpErrorHandler::Run() { 32 0 : if (ValidatePacket() == false) { 33 0 : proto_->increment_drops(); 34 0 : return true; 35 : } 36 : 37 0 : VmInterface *vm_itf = dynamic_cast<VmInterface *> 38 0 : (agent()->interface_table()->FindInterface(GetInterfaceIndex())); 39 0 : if (vm_itf == NULL || vm_itf->layer3_forwarding() == false || 40 0 : vm_itf->vn() == NULL) { 41 0 : proto_->increment_interface_errors(); 42 0 : return true; 43 : } 44 : 45 0 : return SendIcmpError(vm_itf); 46 : } 47 : 48 : // Generate ICMP error 49 0 : bool IcmpErrorHandler::SendIcmpError(VmInterface *intf) { 50 : char data[ICMP_PAYLOAD_LEN]; 51 0 : uint16_t data_len = ntohs(pkt_info_->ip->ip_len); 52 0 : if (data_len > ICMP_PAYLOAD_LEN) 53 0 : data_len = ICMP_PAYLOAD_LEN; 54 0 : memcpy(data, pkt_info_->ip, data_len); 55 : 56 0 : uint32_t src_ip = 0; 57 0 : FlowKey key; 58 0 : bool is_nat_flow = false; 59 : 60 0 : if (pkt_info_->agent_hdr.flow_index == (uint32_t)-1) { 61 : // flow index is -1 for 255.255.255.255 62 0 : if (pkt_info_->ip_daddr.to_v4().to_ulong() != 0xFFFFFFFF) { 63 0 : proto_->increment_invalid_flow_index(); 64 0 : return true; 65 : } 66 0 : src_ip = pkt_info_->ip_saddr.to_v4().to_ulong(); 67 : } else { 68 0 : if (proto_->FlowIndexToKey(pkt_info_->agent_hdr.flow_index, &key, &is_nat_flow) 69 0 : == false || 70 0 : key.family != Address::INET || 71 0 : ((!is_nat_flow) && 72 0 : (key.src_addr != pkt_info_->ip_saddr || 73 0 : key.dst_addr != pkt_info_->ip_daddr || 74 0 : key.protocol != pkt_info_->ip_proto || 75 0 : key.src_port != pkt_info_->sport || 76 0 : key.dst_port != pkt_info_->dport))) { 77 0 : proto_->increment_invalid_flow_index(); 78 0 : return true; 79 : } 80 : 81 0 : src_ip = key.src_addr.to_v4().to_ulong(); 82 : } 83 : 84 : // Get IPAM to find default gateway 85 0 : const VnIpam *ipam = intf->vn()->GetIpam(Ip4Address(src_ip)); 86 0 : if (ipam == NULL || ipam->default_gw.is_v4() == false) { 87 0 : proto_->increment_interface_errors(); 88 0 : return true; 89 : } 90 : 91 : // Retain the agent-header before ethernet header 92 0 : uint16_t len = (char *)pkt_info_->eth - (char *)pkt_info_->pkt; 93 0 : uint16_t buf_len = pkt_info_->max_pkt_len; 94 : 95 : // Form ICMP Packet with following 96 : // EthHdr - IP Header - ICMP Header - User IP Packet(max 128 bytes) 97 0 : char *ptr = (char *)pkt_info_->pkt; 98 0 : len += EthHdr(ptr + len, buf_len - len, 99 : agent()->vhost_interface()->mac(), 100 0 : MacAddress(pkt_info_->eth->ether_shost), 101 0 : ETHERTYPE_IP, intf->tx_vlan_id()); 102 : 103 0 : uint16_t icmp_len = ICMP_UNREACH_HDR_LEN; 104 0 : uint16_t ip_len = sizeof(struct ip) + icmp_len + data_len; 105 0 : len += IpHdr(ptr + len, buf_len - len, ip_len, 106 0 : htonl(ipam->default_gw.to_v4().to_ulong()), 107 : htonl(src_ip), IPPROTO_ICMP, DEFAULT_IP_ID, DEFAULT_IP_TTL); 108 : 109 0 : char *icmp = ptr + len; 110 0 : len += IcmpHdr(ptr + len, buf_len - len, ICMP_UNREACH, 111 0 : ICMP_UNREACH_NEEDFRAG, 0, pkt_info_->agent_hdr.mtu); 112 : 113 0 : if (pkt_info_->agent_hdr.flow_index != (uint32_t)-1) { 114 : // Its possible that user payload has gone thru NAT processing already. 115 : // Restore the original fields from flow_key 116 0 : struct ip *ip = (struct ip *)data; 117 0 : uint16_t ip_hlen = ip->ip_hl * 4; 118 : 119 0 : ip->ip_src.s_addr = htonl(key.src_addr.to_v4().to_ulong()); 120 0 : ip->ip_dst.s_addr = htonl(key.dst_addr.to_v4().to_ulong()); 121 0 : ip->ip_sum = 0; 122 0 : ip->ip_sum = Csum((uint16_t *)data, ip_hlen, 0); 123 0 : if (ip->ip_p == IPPROTO_UDP) { 124 0 : udphdr *udp = (udphdr *)(data + ip_hlen); 125 0 : udp->uh_sport = ntohs(key.src_port); 126 0 : udp->uh_dport = ntohs(key.dst_port); 127 0 : } else if (ip->ip_p == IPPROTO_TCP) { 128 0 : tcphdr *tcp = (tcphdr *)(data + ip_hlen); 129 0 : tcp->th_sport = ntohs(key.src_port); 130 0 : tcp->th_dport = ntohs(key.dst_port); 131 : } 132 : } 133 0 : memcpy(ptr + len, data, data_len); 134 0 : len += data_len; 135 0 : IcmpChecksum(icmp, icmp_len + data_len); 136 0 : pkt_info_->set_len(len); 137 : 138 0 : Send(GetInterfaceIndex(), pkt_info_->vrf, AgentHdr::TX_SWITCH, 139 : PktHandler::ICMP); 140 0 : return true; 141 : }