Seastar
High performance C++ framework for concurrent servers
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
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
33namespace seastar {
34
35namespace net {
36
37class arp;
38class arp_for_protocol;
39template <typename L3>
40class arp_for;
41
43protected:
44 arp& _arp;
45 uint16_t _proto_num;
46public:
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
58class arp {
59 interface* _netif;
60 l3_protocol _proto;
61 std::unordered_map<uint16_t, arp_for_protocol*> _arp_for_protocol;
63private:
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 };
76public:
77 explicit arp(interface* netif);
78 void add(uint16_t proto_num, arp_for_protocol* afp);
79 void del(uint16_t proto_num);
80private:
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
89template <typename L3>
90class arp_for : public arp_for_protocol {
91public:
93 using l3addr = typename L3::address_type;
94private:
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 };
143private:
144 l3addr _l3self = L3::broadcast_address();
145 std::unordered_map<l3addr, l2addr> _table;
146 std::unordered_map<l3addr, resolution> _in_progress;
147private:
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);
153public:
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
169template <typename L3>
170packet
171arp_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
188template <typename L3>
189void 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
193template <typename L3>
194future<>
195arp_for<L3>::send_query(const l3addr& paddr) {
196 send(ethernet::broadcast_address(), make_query_packet(paddr));
197 return make_ready_future<>();
198}
199
200class arp_error : public std::runtime_error {
201public:
202 arp_error(const std::string& msg) : std::runtime_error(msg) {}
203};
204
206public:
207 arp_timeout_error() : arp_error("ARP timeout") {}
208};
209
211public:
212 arp_queue_full_error() : arp_error("ARP waiter's queue is full") {}
213};
214
215template <typename L3>
217arp_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
248template <typename L3>
249void
250arp_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
263template <typename L3>
264future<>
265arp_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
285template <typename L3>
286future<>
287arp_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
A representation of a possibly not-yet-computed value.
Definition: future.hh:1240
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:83
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: ethernet.hh:37