Seastar
High performance C++ framework for concurrent servers
arp.hh
1 /*
2  * This file is open source software, licensed to you under the terms
3  * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4  * distributed with this work for additional information regarding copyright
5  * ownership. You may not use this file except in compliance with the License.
6  *
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an
13  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  * KIND, either express or implied. See the License for the
15  * specific language governing permissions and limitations
16  * under the License.
17  */
18 /*
19  * Copyright (C) 2014 Cloudius Systems, Ltd.
20  *
21  */
22 
23 #pragma once
24 
25 #ifndef SEASTAR_MODULE
26 #include <unordered_map>
27 #endif
28 #include <seastar/net/net.hh>
29 #include <seastar/core/byteorder.hh>
30 #include <seastar/net/ethernet.hh>
31 #include <seastar/util/modules.hh>
32 
33 namespace seastar {
34 
35 namespace net {
36 
37 class arp;
38 class arp_for_protocol;
39 template <typename L3>
40 class arp_for;
41 
43 protected:
44  arp& _arp;
45  uint16_t _proto_num;
46 public:
47  arp_for_protocol(arp& a, uint16_t proto_num);
48  virtual ~arp_for_protocol();
49  virtual future<> received(packet p) = 0;
50  virtual bool forward(forward_hash& out_hash_data, packet& p, size_t off) {
51  std::ignore = out_hash_data;
52  std::ignore = p;
53  std::ignore = off;
54  return false;
55  }
56 };
57 
58 class arp {
59  interface* _netif;
60  l3_protocol _proto;
61  std::unordered_map<uint16_t, arp_for_protocol*> _arp_for_protocol;
63 private:
64  struct arp_hdr {
65  uint16_t htype;
66  uint16_t ptype;
67 
68  static arp_hdr read(const char* p) {
69  arp_hdr ah;
70  ah.htype = consume_be<uint16_t>(p);
71  ah.ptype = consume_be<uint16_t>(p);
72  return ah;
73  }
74  static constexpr size_t size() { return 4; }
75  };
76 public:
77  explicit arp(interface* netif);
78  void add(uint16_t proto_num, arp_for_protocol* afp);
79  void del(uint16_t proto_num);
80 private:
81  ethernet_address l2self() const noexcept { return _netif->hw_address(); }
82  future<> process_packet(packet p, ethernet_address from);
83  bool forward(forward_hash& out_hash_data, packet& p, size_t off);
84  std::optional<l3_protocol::l3packet> get_packet();
85  template <class l3_proto>
86  friend class arp_for;
87 };
88 
89 template <typename L3>
90 class arp_for : public arp_for_protocol {
91 public:
92  using l2addr = ethernet_address;
93  using l3addr = typename L3::address_type;
94 private:
95  static constexpr auto max_waiters = 512;
96  enum oper {
97  op_request = 1,
98  op_reply = 2,
99  };
100  struct arp_hdr {
101  uint16_t htype;
102  uint16_t ptype;
103  uint8_t hlen;
104  uint8_t plen;
105  uint16_t oper;
106  l2addr sender_hwaddr;
107  l3addr sender_paddr;
108  l2addr target_hwaddr;
109  l3addr target_paddr;
110 
111  static arp_hdr read(const char* p) {
112  arp_hdr ah;
113  ah.htype = consume_be<uint16_t>(p);
114  ah.ptype = consume_be<uint16_t>(p);
115  ah.hlen = consume_be<uint8_t>(p);
116  ah.plen = consume_be<uint8_t>(p);
117  ah.oper = consume_be<uint16_t>(p);
118  ah.sender_hwaddr = l2addr::consume(p);
119  ah.sender_paddr = l3addr::consume(p);
120  ah.target_hwaddr = l2addr::consume(p);
121  ah.target_paddr = l3addr::consume(p);
122  return ah;
123  }
124  void write(char* p) const {
125  produce_be<uint16_t>(p, htype);
126  produce_be<uint16_t>(p, ptype);
127  produce_be<uint8_t>(p, hlen);
128  produce_be<uint8_t>(p, plen);
129  produce_be<uint16_t>(p, oper);
130  sender_hwaddr.produce(p);
131  sender_paddr.produce(p);
132  target_hwaddr.produce(p);
133  target_paddr.produce(p);
134  }
135  static constexpr size_t size() {
136  return 8 + 2 * (l2addr::size() + l3addr::size());
137  }
138  };
139  struct resolution {
140  std::vector<promise<l2addr>> _waiters;
141  timer<> _timeout_timer;
142  };
143 private:
144  l3addr _l3self = L3::broadcast_address();
145  std::unordered_map<l3addr, l2addr> _table;
146  std::unordered_map<l3addr, resolution> _in_progress;
147 private:
148  packet make_query_packet(l3addr paddr);
149  virtual future<> received(packet p) override;
150  future<> handle_request(arp_hdr* ah);
151  l2addr l2self() const noexcept { return _arp.l2self(); }
152  void send(l2addr to, packet p);
153 public:
154  future<> send_query(const l3addr& paddr);
155  explicit arp_for(arp& a) : arp_for_protocol(a, L3::arp_protocol_type()) {
156  _table[L3::broadcast_address()] = ethernet::broadcast_address();
157  }
158  future<ethernet_address> lookup(const l3addr& addr);
159  void learn(l2addr l2, l3addr l3);
160  void run();
161  void set_self_addr(l3addr addr) {
162  _table.erase(_l3self);
163  _table[addr] = l2self();
164  _l3self = addr;
165  }
166  friend class arp;
167 };
168 
169 template <typename L3>
170 packet
171 arp_for<L3>::make_query_packet(l3addr paddr) {
172  arp_hdr hdr;
173  hdr.htype = ethernet::arp_hardware_type();
174  hdr.ptype = L3::arp_protocol_type();
175  hdr.hlen = sizeof(l2addr);
176  hdr.plen = sizeof(l3addr);
177  hdr.oper = op_request;
178  hdr.sender_hwaddr = l2self();
179  hdr.sender_paddr = _l3self;
180  hdr.target_hwaddr = ethernet::broadcast_address();
181  hdr.target_paddr = paddr;
182  auto p = packet();
183  p.prepend_uninitialized_header(hdr.size());
184  hdr.write(p.get_header(0, hdr.size()));
185  return p;
186 }
187 
188 template <typename L3>
189 void arp_for<L3>::send(l2addr to, packet p) {
190  _arp._packetq.push_back(l3_protocol::l3packet{eth_protocol_num::arp, to, std::move(p)});
191 }
192 
193 template <typename L3>
194 future<>
195 arp_for<L3>::send_query(const l3addr& paddr) {
196  send(ethernet::broadcast_address(), make_query_packet(paddr));
197  return make_ready_future<>();
198 }
199 
200 class arp_error : public std::runtime_error {
201 public:
202  arp_error(const std::string& msg) : std::runtime_error(msg) {}
203 };
204 
205 class arp_timeout_error : public arp_error {
206 public:
207  arp_timeout_error() : arp_error("ARP timeout") {}
208 };
209 
211 public:
212  arp_queue_full_error() : arp_error("ARP waiter's queue is full") {}
213 };
214 
215 template <typename L3>
217 arp_for<L3>::lookup(const l3addr& paddr) {
218  auto i = _table.find(paddr);
219  if (i != _table.end()) {
220  return make_ready_future<ethernet_address>(i->second);
221  }
222  auto j = _in_progress.find(paddr);
223  auto first_request = j == _in_progress.end();
224  auto& res = first_request ? _in_progress[paddr] : j->second;
225 
226  if (first_request) {
227  res._timeout_timer.set_callback([paddr, this, &res] {
228  // FIXME: future is discarded
229  (void)send_query(paddr);
230  for (auto& w : res._waiters) {
231  w.set_exception(arp_timeout_error());
232  }
233  res._waiters.clear();
234  });
235  res._timeout_timer.arm_periodic(std::chrono::seconds(1));
236  // FIXME: future is discarded
237  (void)send_query(paddr);
238  }
239 
240  if (res._waiters.size() >= max_waiters) {
241  return make_exception_future<ethernet_address>(arp_queue_full_error());
242  }
243 
244  res._waiters.emplace_back();
245  return res._waiters.back().get_future();
246 }
247 
248 template <typename L3>
249 void
250 arp_for<L3>::learn(l2addr hwaddr, l3addr paddr) {
251  _table[paddr] = hwaddr;
252  auto i = _in_progress.find(paddr);
253  if (i != _in_progress.end()) {
254  auto& res = i->second;
255  res._timeout_timer.cancel();
256  for (auto &&pr : res._waiters) {
257  pr.set_value(hwaddr);
258  }
259  _in_progress.erase(i);
260  }
261 }
262 
263 template <typename L3>
264 future<>
265 arp_for<L3>::received(packet p) {
266  auto ah = p.get_header(0, arp_hdr::size());
267  if (!ah) {
268  return make_ready_future<>();
269  }
270  auto h = arp_hdr::read(ah);
271  if (h.hlen != sizeof(l2addr) || h.plen != sizeof(l3addr)) {
272  return make_ready_future<>();
273  }
274  switch (h.oper) {
275  case op_request:
276  return handle_request(&h);
277  case op_reply:
278  arp_learn(h.sender_hwaddr, h.sender_paddr);
279  return make_ready_future<>();
280  default:
281  return make_ready_future<>();
282  }
283 }
284 
285 template <typename L3>
286 future<>
287 arp_for<L3>::handle_request(arp_hdr* ah) {
288  if (ah->target_paddr == _l3self
289  && _l3self != L3::broadcast_address()) {
290  ah->oper = op_reply;
291  ah->target_hwaddr = ah->sender_hwaddr;
292  ah->target_paddr = ah->sender_paddr;
293  ah->sender_hwaddr = l2self();
294  ah->sender_paddr = _l3self;
295  auto p = packet();
296  ah->write(p.prepend_uninitialized_header(ah->size()));
297  send(ah->target_hwaddr, std::move(p));
298  }
299  return make_ready_future<>();
300 }
301 
302 }
303 
304 }
Definition: circular_buffer.hh:63
Definition: arp.hh:200
Definition: arp.hh:42
Definition: arp.hh:90
Definition: arp.hh:205
Definition: arp.hh:58
Definition: net.hh:51
Definition: net.hh:114
Definition: net.hh:94
Definition: packet.hh:87
Definition: timer.hh:84
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: ethernet.hh:37