xref: /mOS-networking-stack/core/src/icmp.c (revision 76404edc)
1 #include <stdint.h>
2 #include <sys/types.h>
3 #include <netinet/ip.h>
4 #include <string.h>
5 
6 #include "mtcp.h"
7 #include "icmp.h"
8 #include "eth_out.h"
9 #include "ip_in.h"
10 #include "ip_out.h"
11 #include "debug.h"
12 #include "arp.h"
13 #include "timer.h"
14 #include "config.h"
15 
16 #define IP_NEXT_PTR(iph) ((uint8_t *)iph + (iph->ihl << 2))
17 
18 /*----------------------------------------------------------------------------*/
19 
20 /*
21 struct icmphdr
22 {
23     uint8_t icmp_type;
24     uint8_t icmp_code;
25     uint16_t icmp_checksum;
26     uint16_t icmp_id;
27     uint16_t icmp_sequence;
28 };
29 */
30 
31 /*----------------------------------------------------------------------------*/
32 static uint16_t
33 ICMPChecksum(uint16_t *icmph, int len)
34 {
35     assert(len >= 0);
36 
37     uint16_t ret = 0;
38     uint32_t sum = 0;
39     uint16_t odd_byte;
40 
41     while (len > 1) {
42         sum += *icmph++;
43         len -= 2;
44     }
45 
46     if (len == 1) {
47         *(uint8_t*)(&odd_byte) = * (uint8_t*)icmph;
48         sum += odd_byte;
49     }
50 
51     sum =  (sum >> 16) + (sum & 0xffff);
52     sum += (sum >> 16);
53     ret =  ~sum;
54 
55     return ret;
56 }
57 /*----------------------------------------------------------------------------*/
58 static uint8_t*
59 ICMPOutput(struct mtcp_manager *mtcp, struct pkt_ctx *pctx, uint32_t saddr, uint32_t daddr,
60         uint8_t icmp_type, uint8_t icmp_code, uint16_t icmp_id, uint16_t icmp_seq,
61         uint8_t *icmpd, uint16_t len)
62 {
63     struct iphdr *iph;
64     int32_t nif;
65     uint8_t *haddr;
66     struct icmphdr *icmph;
67     uint32_t pktlen = sizeof(struct iphdr) + sizeof(struct icmphdr) + len;
68     struct timeval cur_ts = {0};
69     uint32_t ts;
70 
71     /* Get hardware interface to forward the packet*/
72     nif = GetOutputInterface(daddr);
73     if (nif < 0)
74         return (uint8_t *) ERROR;
75 
76     /* Get next hop MAC address */
77     haddr = GetDestinationHWaddr(daddr);
78     if (!haddr) {
79         uint8_t *da = (uint8_t *)&daddr;
80         TRACE_INFO("[WARNING] The destination IP %u.%u.%u.%u "
81                    "is not in ARP table!\n",
82                    da[0], da[1], da[2], da[3]);
83 	/* ARP requests will not be created if it's a standalone middlebox */
84 	if (!pctx->forward)
85 		RequestARP(mtcp, daddr, nif, mtcp->cur_ts);
86         haddr = GetDestinationHWaddr(daddr);
87     }
88 
89     /* Check if we have valid next hop address */
90     if(!haddr)
91         return (uint8_t *) ERROR;
92 
93     /* Set up timestamp for EthernetOutput */
94     gettimeofday(&cur_ts, NULL);
95     ts = TIMEVAL_TO_TS(&cur_ts);
96 
97     /* Allocate a buffer */
98     iph = (struct iphdr *)EthernetOutput(mtcp, pctx, ETH_P_IP, nif, haddr, pktlen, ts);
99     if (!iph)
100         return (uint8_t *) ERROR;
101 
102     /* Fill in the ip header */
103     iph->ihl = IP_HEADER_LEN >> 2;
104     iph->version = 4;
105     iph->tos = 0;
106     iph->tot_len = htons(pktlen);
107     iph->id = htons(0);
108     iph->frag_off = htons(IP_DF);
109     iph->ttl = 64;
110     iph->protocol = IPPROTO_ICMP;
111     iph->saddr = saddr;
112     iph->daddr = daddr;
113     iph->check = 0;
114     iph->check = ip_fast_csum(iph, iph->ihl);
115 
116     icmph = (struct icmphdr *) IP_NEXT_PTR(iph);
117 
118     /* Fill in the icmp header */
119     icmph->icmp_type = icmp_type;
120     icmph->icmp_code = icmp_code;
121     icmph->icmp_checksum = 0;
122     ICMP_ECHO_SET_ID(icmph, htons(icmp_id));
123     ICMP_ECHO_SET_SEQ(icmph, htons(icmp_seq));
124 
125     /* Fill in the icmp data */
126     if(len > 0)
127         memcpy((void *) (icmph + 1), icmpd, len);
128 
129     /* Calculate ICMP Checksum with header and data */
130     icmph->icmp_checksum =
131                        ICMPChecksum((uint16_t *)icmph, sizeof(struct icmphdr) + len);
132 
133 #if DBGMSG
134     DumpICMPPacket(icmph, saddr, daddr);
135 #endif
136     return (uint8_t *)(iph + 1);
137 }
138 /*----------------------------------------------------------------------------*/
139 void
140 RequestICMP(mtcp_manager_t mtcp, struct pkt_ctx *pctx, uint32_t saddr, uint32_t daddr,
141         uint16_t icmp_id, uint16_t icmp_sequence,
142         uint8_t *icmpd, uint16_t len)
143 {
144     /* send icmp request with given parameters */
145     ICMPOutput(mtcp, pctx, saddr, daddr, ICMP_ECHO, 0, ntohs(icmp_id), ntohs(icmp_sequence),
146                icmpd, len);
147 }
148 /*----------------------------------------------------------------------------*/
149 static int
150 ProcessICMPECHORequest(mtcp_manager_t mtcp, struct pkt_ctx *pctx, struct icmphdr *icmph)
151 {
152     int ret = 0;
153 
154     /* Check correctness of ICMP checksum and send ICMP echo reply */
155     if (ICMPChecksum((uint16_t *) icmph, pctx->p.ip_len - (pctx->p.iph->ihl << 2) ))
156         ret = ERROR;
157     else
158         ICMPOutput(mtcp, pctx, pctx->p.iph->daddr, pctx->p.iph->saddr, ICMP_ECHOREPLY, 0,
159                    ntohs(ICMP_ECHO_GET_ID(icmph)), ntohs(ICMP_ECHO_GET_SEQ(icmph)), (uint8_t *) (icmph + 1),
160                    (uint16_t) (pctx->p.ip_len - (pctx->p.iph->ihl << 2) - sizeof(struct icmphdr)) );
161 
162     return ret;
163 }
164 /*----------------------------------------------------------------------------*/
165 static int
166 ProcessICMPECHOReply(mtcp_manager_t mtcp, struct pkt_ctx *pctx, struct icmphdr *icmph)
167 {
168 
169     /* XXX We can allow sending ping request from mOS app. IMHO, it's bad */
170 
171 #if 0
172     uint8_t type, code;
173     uint16_t seq, id;
174 
175     /* Extract ICMP field from packet */
176     type = icmph->icmp_type;
177     code = icmph->icmp_code;
178     seq  = ntohs(ICMP_ECHO_GET_SEQ(icmph));
179     id   = ntohs(ICMP_ECHO_GET_ID(icmph));
180 #endif
181     return 0;
182 }
183 /*----------------------------------------------------------------------------*/
184 int
185 ProcessICMPPacket(mtcp_manager_t mtcp, struct pkt_ctx *pctx)
186 {
187     struct icmphdr *icmph = (struct icmphdr *) IP_NEXT_PTR(pctx->p.iph);
188     int i;
189     int to_me = FALSE;
190 
191     /* process the icmp messages destined to me */
192     for (i = 0; i < g_config.mos->netdev_table->num; i++) {
193         if (pctx->p.iph->daddr == g_config.mos->netdev_table->ent[i]->ip_addr) {
194             to_me = TRUE;
195         }
196     }
197 
198     if (!to_me)
199         return FALSE;
200 
201     switch (icmph->icmp_type)
202     {
203         case ICMP_ECHO:
204             ProcessICMPECHORequest(mtcp, pctx, icmph);
205             break;
206 
207         case ICMP_ECHOREPLY:
208             ProcessICMPECHOReply(mtcp, pctx, icmph);
209             break;
210 
211         case ICMP_DEST_UNREACH:
212             TRACE_INFO("[INFO] ICMP Destination Unreachable message received\n");
213             break;
214 
215         case ICMP_TIME_EXCEEDED:
216             TRACE_INFO("[INFO] ICMP Time Exceeded message received\n");
217             break;
218 
219         default:
220             TRACE_INFO("[INFO] Unsupported ICMP message type %x received\n",
221                        icmph->icmp_type);
222             break;
223     }
224 
225     return TRUE;
226 }
227 /*----------------------------------------------------------------------------*/
228 void
229 DumpICMPPacket(struct icmphdr *icmph, uint32_t saddr, uint32_t daddr)
230 {
231     uint8_t *t;
232 
233     fprintf(stderr, "ICMP header: \n");
234     fprintf(stderr, "Type: %d, "
235             "Code: %d, ID: %d, Sequence: %d\n",
236             icmph->icmp_type, icmph->icmp_code,
237             ICMP_ECHO_GET_ID(icmph), ICMP_ECHO_GET_SEQ(icmph));
238 
239     t = (uint8_t *)&saddr;
240     fprintf(stderr, "Sender IP: %u.%u.%u.%u\n",
241             t[0], t[1], t[2], t[3]);
242 
243     t = (uint8_t *)&daddr;
244     fprintf(stderr, "Target IP: %u.%u.%u.%u\n",
245             t[0], t[1], t[2], t[3]);
246 }
247 /*----------------------------------------------------------------------------*/
248 #undef IP_NEXT_PTR
249