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