TAS
TCP Acceleration as an OS Service
arp.c
1 /*
2  * Copyright 2019 University of Washington, Max Planck Institute for
3  * Software Systems, and The University of Texas at Austin
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include <tas.h>
32 #include <packet_defs.h>
33 #include <utils.h>
34 #include "internal.h"
35 
36 #define ARP_DEBUG(x...) do { } while (0)
37 /*#define ARP_DEBUG(x...) fprintf(stderr, "arp: " x)*/
38 
39 struct arp_entry {
40  int status;
41  uint32_t ip;
42  uint8_t mac[ETH_ADDR_LEN];
43  struct nicif_completion *compl;
44 
45  uint32_t timeout;
46  struct timeout to;
47 
48  struct arp_entry *prev;
49  struct arp_entry *next;
50 };
51 
52 static inline int response_tx(const void *dst_mac, uint32_t dst_ip);
53 static inline int request_tx(uint32_t dst_ip);
54 static inline struct arp_entry *ae_lookup(uint32_t ip);
55 
56 static struct arp_entry *arp_table = NULL;
57 
58 int arp_init(void)
59 {
60  uint64_t mac;
61  struct arp_entry *lb = malloc(sizeof(struct arp_entry));
62  assert(lb != NULL);
63 
64  lb->status = 0;
65  lb->ip = config.ip;
66  memcpy(lb->mac, &eth_addr, ETH_ADDR_LEN);
67  lb->compl = NULL;
68  lb->next = NULL;
69  lb->prev = NULL;
70  arp_table = lb;
71 
72  mac = 0;
73  memcpy(&mac, &eth_addr, ETH_ADDR_LEN);
74 
75  if (!config.quiet)
76  printf("host ip: %x MAC: %lx\n", config.ip, mac);
77 
78  return 0;
79 }
80 
81 int arp_request(struct nicif_completion *comp, uint32_t ip, uint64_t *mac)
82 {
83  struct arp_entry *ae;
84 
85  *mac = 0;
86 
87  /* found entry */
88  if ((ae = ae_lookup(ip)) != NULL) {
89  if (ae->status == 0) {
90  ARP_DEBUG("lookup succeeded (%x)\n", ip);
91  memcpy(mac, ae->mac, 6);
92  return 0;
93  } else {
94  /* request still pending */
95  ARP_DEBUG("request still pending (%x)\n", ip);
96  comp->ptr = mac;
97  comp->el.next = (void *) ae->compl;
98  ae->compl = comp;
99  return 1;
100  }
101  }
102 
103  /* allocate cache entry */
104  if ((ae = malloc(sizeof(*ae))) == NULL) {
105  fprintf(stderr, "arp_request: malloc failed\n");
106  return -1;
107  }
108 
109  ae->status = 1;
110  ae->ip = ip;
111  ae->compl = comp;
112  comp->el.next = NULL;
113  comp->ptr = mac;
114 
115  /* send out request */
116  if (request_tx(ip) != 0) {
117  /* timeout will take care of re-trying */
118  fprintf(stderr, "arp_timeout: sending out request failed\n");
119  }
120 
121  /* arm timeout */
122  ae->timeout = config.arp_to;
123  util_timeout_arm(&timeout_mgr, &ae->to, ae->timeout, TO_ARP_REQ);
124 
125  /* insert into list */
126  ae->prev = NULL;
127  ae->next = arp_table;
128  if (arp_table != NULL) {
129  arp_table->prev = ae;
130  }
131  arp_table = ae;
132 
133  ARP_DEBUG("request sent (%x)\n", ip);
134 
135  return 1;
136 }
137 
138 void arp_packet(const void *pkt, uint16_t len)
139 {
140  const struct pkt_arp *parp = pkt;
141  const struct arp_hdr *arp = &parp->arp;
142  uint16_t op;
143  uint64_t cnt = 1;
144  int fd, ret;
145  struct arp_entry *ae;
146  struct nicif_completion *comp, *comp_next;
147 
148  /* filter out bad packets */
149  if (f_beui16(arp->htype) != ARP_HTYPE_ETHERNET ||
150  f_beui16(arp->ptype) != ARP_PTYPE_IPV4 ||
151  arp->hlen != 6 || arp->plen != 4)
152  {
153  fprintf(stderr, "arp_packet: Invalid packet received htype=%x ptype=%x "
154  "hlen=%x plen=%x\n", f_beui16(arp->htype), f_beui16(arp->ptype),
155  arp->hlen, arp->plen);
156  return;
157  }
158  op = f_beui16(arp->oper);
159  if (op == ARP_OPER_REQUEST) {
160  /* handle ARP request */
161  ARP_DEBUG("arp request received (%x)\n", f_beui32(arp->spa));
162  if (f_beui32(arp->tpa) != config.ip) {
163  /* ARP request not for me */
164  return;
165  }
166 
167  /* send response */
168  if (response_tx(&arp->sha, f_beui32(arp->spa)) != 0) {
169  fprintf(stderr, "arp_packet: sending response failed\n");
170  return;
171  }
172  } else if (op == ARP_OPER_REPLY) {
173  ARP_DEBUG("arp reply received (%x)\n", f_beui32(arp->spa));
174 
175  /* handle ARP response */
176  if ((ae = ae_lookup(f_beui32(arp->spa))) == NULL) {
177  ARP_DEBUG(stderr, "arp_packet: response has no entry\n");
178  return;
179  }
180 
181  if (ae->status == 1) {
182  /* disarm timeout */
183  util_timeout_disarm(&timeout_mgr, &ae->to);
184  }
185 
186  /* fill in information on arp entry */
187  memcpy(ae->mac, &arp->sha, ETH_ADDR_LEN);
188  ae->status = 0;
189 
190  /* notify waiting connections */
191  for (comp = ae->compl; comp != NULL; comp = comp_next) {
192  comp_next = (void *) comp->el.next;
193 
194  memcpy(comp->ptr, ae->mac, ETH_ADDR_LEN);
195  comp->status = 0;
196  fd = comp->notify_fd;
197  nbqueue_enq(comp->q, &comp->el);
198  if (fd != -1) {
199  ret = write(fd, &cnt, sizeof(cnt));
200  if (ret <= 0) {
201  perror("arp_packet: error writing to notify fd");
202  }
203  }
204  }
205  ae->compl = NULL;
206  }
207 }
208 
209 void arp_timeout(struct timeout *to, enum timeout_type type)
210 {
211  int fd;
212  ssize_t ret;
213  uint64_t cnt = 1;
214  struct nicif_completion *comp, *comp_next;
215  struct arp_entry *ae = (struct arp_entry *)
216  ((uintptr_t) to - offsetof(struct arp_entry, to));
217 
218  ARP_DEBUG("arp_timeout(%x): timeout=%uus\n", ae->ip, ae->timeout);
219 
220  /* the arp entry should not be ready or the timeout would have been
221  * cancelled */
222  if (ae->status == 0) {
223  fprintf(stderr, "arp_timeout: arp entry marked as ready\n");
224  abort();
225  }
226 
227  /* if we received the maximum timeout, abort */
228  if (ae->timeout * 2 >= config.arp_to_max) {
229  ARP_DEBUG("arp_timeout: request for %x timed out\n", ae->ip);
230 
231  /* notify waiting connections */
232  for (comp = ae->compl; comp != NULL; comp = comp_next) {
233  comp_next = (void *) comp->el.next;
234 
235  comp->status = -1;
236  fd = comp->notify_fd;
237  nbqueue_enq(comp->q, &comp->el);
238  if (fd != -1) {
239  ret = write(fd, &cnt, sizeof(cnt));
240  if (ret <= 0) {
241  perror("arp_packet: error writing to notify fd");
242  }
243  }
244  }
245 
246  /* remove arp entry from cache */
247  if (ae->prev != NULL) {
248  ae->prev->next = ae->next;
249  } else {
250  arp_table = ae->next;
251  }
252  if (ae->next != NULL) {
253  ae->next->prev = ae->prev;
254  }
255 
256  /* free entry */
257  free(ae);
258  return;
259  }
260 
261  /* send out another request */
262  if (request_tx(ae->ip) != 0) {
263  fprintf(stderr, "arp_timeout: sending out request failed\n");
264  }
265 
266  /* rearm timeout */
267  ae->timeout *= 2;
268  util_timeout_arm(&timeout_mgr, &ae->to, ae->timeout, TO_ARP_REQ);
269 }
270 
271 static inline int response_tx(const void *dst_mac, uint32_t dst_ip)
272 {
273  struct pkt_arp *parp_out;
274  uint32_t new_tail;
275 
276  /* allocate tx buffer */
277  if (nicif_tx_alloc(sizeof(*parp_out), (void **) &parp_out, &new_tail) != 0) {
278  return -1;
279  }
280 
281  /* fill in response */
282  memcpy(&parp_out->eth.src, &eth_addr, ETH_ADDR_LEN);
283  memcpy(&parp_out->arp.sha, &eth_addr, ETH_ADDR_LEN);
284  memcpy(&parp_out->eth.dest, dst_mac, ETH_ADDR_LEN);
285  memcpy(&parp_out->arp.tha, dst_mac, ETH_ADDR_LEN);
286  parp_out->arp.spa = t_beui32(config.ip);
287  parp_out->arp.tpa = t_beui32(dst_ip);
288 
289  parp_out->eth.type = t_beui16(ETH_TYPE_ARP);
290 
291  parp_out->arp.htype = t_beui16(ARP_HTYPE_ETHERNET);
292  parp_out->arp.ptype = t_beui16(ARP_PTYPE_IPV4);
293  parp_out->arp.hlen = 6;
294  parp_out->arp.plen = 4;
295  parp_out->arp.oper = t_beui16(ARP_OPER_REPLY);
296 
297  nicif_tx_send(new_tail, 0);
298 
299  return 0;
300 }
301 
302 static inline int request_tx(uint32_t dst_ip)
303 {
304  struct pkt_arp *parp_out;
305  uint32_t new_tail;
306  uint64_t dst_mac = 0xffffffffffffULL;
307 
308  /* allocate tx buffer */
309  if (nicif_tx_alloc(sizeof(*parp_out), (void **) &parp_out, &new_tail) != 0) {
310  return -1;
311  }
312 
313  /* fill in response */
314  memcpy(&parp_out->eth.src, &eth_addr, ETH_ADDR_LEN);
315  memcpy(&parp_out->arp.sha, &eth_addr, ETH_ADDR_LEN);
316  memcpy(&parp_out->eth.dest, &dst_mac, ETH_ADDR_LEN);
317  memcpy(&parp_out->arp.tha, &dst_mac, ETH_ADDR_LEN);
318  parp_out->arp.spa = t_beui32(config.ip);
319  parp_out->arp.tpa = t_beui32(dst_ip);
320 
321  parp_out->eth.type = t_beui16(ETH_TYPE_ARP);
322 
323  parp_out->arp.htype = t_beui16(ARP_HTYPE_ETHERNET);
324  parp_out->arp.ptype = t_beui16(ARP_PTYPE_IPV4);
325  parp_out->arp.hlen = 6;
326  parp_out->arp.plen = 4;
327  parp_out->arp.oper = t_beui16(ARP_OPER_REQUEST);
328 
329  nicif_tx_send(new_tail, 0);
330 
331  return 0;
332 }
333 
334 static inline struct arp_entry *ae_lookup(uint32_t ip)
335 {
336  struct arp_entry *ae;
337 
338  for (ae = arp_table; ae != NULL; ae = ae->next) {
339  if (ae->ip == ip) {
340  return ae;
341  }
342  }
343  return NULL;
344 }
int arp_request(struct nicif_completion *comp, uint32_t ip, uint64_t *mac)
Definition: arp.c:81
void arp_packet(const void *pkt, uint16_t len)
Definition: arp.c:138
void arp_timeout(struct timeout *to, enum timeout_type type)
Definition: arp.c:209
void util_timeout_disarm(struct timeout_manager *mgr, struct timeout *to)
Definition: timeout.c:155
int nicif_tx_alloc(uint16_t len, void **buf, uint32_t *opaque)
Definition: nicif.c:352
Definition: arp.c:39
uint32_t arp_to
Definition: config.h:73
uint32_t ip
Definition: config.h:67
int arp_init(void)
Definition: arp.c:58
uint32_t arp_to_max
Definition: config.h:75
void nicif_tx_send(uint32_t opaque, int no_ts)
Definition: nicif.c:368
void util_timeout_arm(struct timeout_manager *mgr, struct timeout *to, uint32_t us, uint8_t type)
Definition: timeout.c:110