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