]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/net/arp.hh
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / include / seastar / net / 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 #include <seastar/net/net.hh>
26 #include <seastar/core/reactor.hh>
27 #include <seastar/core/byteorder.hh>
28 #include <seastar/net/ethernet.hh>
29 #include <unordered_map>
30
31 namespace seastar {
32
33 namespace net {
34
35 class arp;
36 class arp_for_protocol;
37 template <typename L3>
38 class arp_for;
39
40 class arp_for_protocol {
41 protected:
42 arp& _arp;
43 uint16_t _proto_num;
44 public:
45 arp_for_protocol(arp& a, uint16_t proto_num);
46 virtual ~arp_for_protocol();
47 virtual future<> received(packet p) = 0;
48 virtual bool forward(forward_hash& out_hash_data, packet& p, size_t off) { return false; }
49 };
50
51 class arp {
52 interface* _netif;
53 l3_protocol _proto;
54 std::unordered_map<uint16_t, arp_for_protocol*> _arp_for_protocol;
55 circular_buffer<l3_protocol::l3packet> _packetq;
56 private:
57 struct arp_hdr {
58 uint16_t htype;
59 uint16_t ptype;
60
61 static arp_hdr read(const char* p) {
62 arp_hdr ah;
63 ah.htype = consume_be<uint16_t>(p);
64 ah.ptype = consume_be<uint16_t>(p);
65 return ah;
66 }
67 static constexpr size_t size() { return 4; }
68 };
69 public:
70 explicit arp(interface* netif);
71 void add(uint16_t proto_num, arp_for_protocol* afp);
72 void del(uint16_t proto_num);
73 private:
74 ethernet_address l2self() { return _netif->hw_address(); }
75 future<> process_packet(packet p, ethernet_address from);
76 bool forward(forward_hash& out_hash_data, packet& p, size_t off);
77 compat::optional<l3_protocol::l3packet> get_packet();
78 template <class l3_proto>
79 friend class arp_for;
80 };
81
82 template <typename L3>
83 class arp_for : public arp_for_protocol {
84 public:
85 using l2addr = ethernet_address;
86 using l3addr = typename L3::address_type;
87 private:
88 static constexpr auto max_waiters = 512;
89 enum oper {
90 op_request = 1,
91 op_reply = 2,
92 };
93 struct arp_hdr {
94 uint16_t htype;
95 uint16_t ptype;
96 uint8_t hlen;
97 uint8_t plen;
98 uint16_t oper;
99 l2addr sender_hwaddr;
100 l3addr sender_paddr;
101 l2addr target_hwaddr;
102 l3addr target_paddr;
103
104 static arp_hdr read(const char* p) {
105 arp_hdr ah;
106 ah.htype = consume_be<uint16_t>(p);
107 ah.ptype = consume_be<uint16_t>(p);
108 ah.hlen = consume_be<uint8_t>(p);
109 ah.plen = consume_be<uint8_t>(p);
110 ah.oper = consume_be<uint16_t>(p);
111 ah.sender_hwaddr = l2addr::consume(p);
112 ah.sender_paddr = l3addr::consume(p);
113 ah.target_hwaddr = l2addr::consume(p);
114 ah.target_paddr = l3addr::consume(p);
115 return ah;
116 }
117 void write(char* p) const {
118 produce_be<uint16_t>(p, htype);
119 produce_be<uint16_t>(p, ptype);
120 produce_be<uint8_t>(p, hlen);
121 produce_be<uint8_t>(p, plen);
122 produce_be<uint16_t>(p, oper);
123 sender_hwaddr.produce(p);
124 sender_paddr.produce(p);
125 target_hwaddr.produce(p);
126 target_paddr.produce(p);
127 }
128 static constexpr size_t size() {
129 return 8 + 2 * (l2addr::size() + l3addr::size());
130 }
131 };
132 struct resolution {
133 std::vector<promise<l2addr>> _waiters;
134 timer<> _timeout_timer;
135 };
136 private:
137 l3addr _l3self = L3::broadcast_address();
138 std::unordered_map<l3addr, l2addr> _table;
139 std::unordered_map<l3addr, resolution> _in_progress;
140 private:
141 packet make_query_packet(l3addr paddr);
142 virtual future<> received(packet p) override;
143 future<> handle_request(arp_hdr* ah);
144 l2addr l2self() { return _arp.l2self(); }
145 void send(l2addr to, packet p);
146 public:
147 future<> send_query(const l3addr& paddr);
148 explicit arp_for(arp& a) : arp_for_protocol(a, L3::arp_protocol_type()) {
149 _table[L3::broadcast_address()] = ethernet::broadcast_address();
150 }
151 future<ethernet_address> lookup(const l3addr& addr);
152 void learn(l2addr l2, l3addr l3);
153 void run();
154 void set_self_addr(l3addr addr) {
155 _table.erase(_l3self);
156 _table[addr] = l2self();
157 _l3self = addr;
158 }
159 friend class arp;
160 };
161
162 template <typename L3>
163 packet
164 arp_for<L3>::make_query_packet(l3addr paddr) {
165 arp_hdr hdr;
166 hdr.htype = ethernet::arp_hardware_type();
167 hdr.ptype = L3::arp_protocol_type();
168 hdr.hlen = sizeof(l2addr);
169 hdr.plen = sizeof(l3addr);
170 hdr.oper = op_request;
171 hdr.sender_hwaddr = l2self();
172 hdr.sender_paddr = _l3self;
173 hdr.target_hwaddr = ethernet::broadcast_address();
174 hdr.target_paddr = paddr;
175 auto p = packet();
176 p.prepend_uninitialized_header(hdr.size());
177 hdr.write(p.get_header(0, hdr.size()));
178 return p;
179 }
180
181 template <typename L3>
182 void arp_for<L3>::send(l2addr to, packet p) {
183 _arp._packetq.push_back(l3_protocol::l3packet{eth_protocol_num::arp, to, std::move(p)});
184 }
185
186 template <typename L3>
187 future<>
188 arp_for<L3>::send_query(const l3addr& paddr) {
189 send(ethernet::broadcast_address(), make_query_packet(paddr));
190 return make_ready_future<>();
191 }
192
193 class arp_error : public std::runtime_error {
194 public:
195 arp_error(const std::string& msg) : std::runtime_error(msg) {}
196 };
197
198 class arp_timeout_error : public arp_error {
199 public:
200 arp_timeout_error() : arp_error("ARP timeout") {}
201 };
202
203 class arp_queue_full_error : public arp_error {
204 public:
205 arp_queue_full_error() : arp_error("ARP waiter's queue is full") {}
206 };
207
208 template <typename L3>
209 future<ethernet_address>
210 arp_for<L3>::lookup(const l3addr& paddr) {
211 auto i = _table.find(paddr);
212 if (i != _table.end()) {
213 return make_ready_future<ethernet_address>(i->second);
214 }
215 auto j = _in_progress.find(paddr);
216 auto first_request = j == _in_progress.end();
217 auto& res = first_request ? _in_progress[paddr] : j->second;
218
219 if (first_request) {
220 res._timeout_timer.set_callback([paddr, this, &res] {
221 // FIXME: future is discarded
222 (void)send_query(paddr);
223 for (auto& w : res._waiters) {
224 w.set_exception(arp_timeout_error());
225 }
226 res._waiters.clear();
227 });
228 res._timeout_timer.arm_periodic(std::chrono::seconds(1));
229 // FIXME: future is discarded
230 (void)send_query(paddr);
231 }
232
233 if (res._waiters.size() >= max_waiters) {
234 return make_exception_future<ethernet_address>(arp_queue_full_error());
235 }
236
237 res._waiters.emplace_back();
238 return res._waiters.back().get_future();
239 }
240
241 template <typename L3>
242 void
243 arp_for<L3>::learn(l2addr hwaddr, l3addr paddr) {
244 _table[paddr] = hwaddr;
245 auto i = _in_progress.find(paddr);
246 if (i != _in_progress.end()) {
247 auto& res = i->second;
248 res._timeout_timer.cancel();
249 for (auto &&pr : res._waiters) {
250 pr.set_value(hwaddr);
251 }
252 _in_progress.erase(i);
253 }
254 }
255
256 template <typename L3>
257 future<>
258 arp_for<L3>::received(packet p) {
259 auto ah = p.get_header(0, arp_hdr::size());
260 if (!ah) {
261 return make_ready_future<>();
262 }
263 auto h = arp_hdr::read(ah);
264 if (h.hlen != sizeof(l2addr) || h.plen != sizeof(l3addr)) {
265 return make_ready_future<>();
266 }
267 switch (h.oper) {
268 case op_request:
269 return handle_request(&h);
270 case op_reply:
271 arp_learn(h.sender_hwaddr, h.sender_paddr);
272 return make_ready_future<>();
273 default:
274 return make_ready_future<>();
275 }
276 }
277
278 template <typename L3>
279 future<>
280 arp_for<L3>::handle_request(arp_hdr* ah) {
281 if (ah->target_paddr == _l3self
282 && _l3self != L3::broadcast_address()) {
283 ah->oper = op_reply;
284 ah->target_hwaddr = ah->sender_hwaddr;
285 ah->target_paddr = ah->sender_paddr;
286 ah->sender_hwaddr = l2self();
287 ah->sender_paddr = _l3self;
288 auto p = packet();
289 ah->write(p.prepend_uninitialized_header(ah->size()));
290 send(ah->target_hwaddr, std::move(p));
291 }
292 return make_ready_future<>();
293 }
294
295 }
296
297 }