Skip to content

Commit 0129500

Browse files
authored
Merge pull request #494 from GalSagie/add_arp_responder_module
Add ARP Responder module
2 parents e9a9b8b + 103164b commit 0129500

4 files changed

Lines changed: 151 additions & 0 deletions

File tree

bessctl/conf/samples/arp.bess

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scapy.all as scapy
2+
3+
eth_header = scapy.Ether(src='02:1e:67:9f:4d:ae', dst='ff:ff:ff:ff:ff:ff')
4+
arp_header = scapy.ARP(op=1, pdst='1.2.3.4')
5+
pkt = eth_header/arp_header
6+
packets = [str(pkt)]
7+
8+
arp::ArpResponder()
9+
arp.add(ip='1.2.3.4', mac_addr='A0:22:33:44:55:66')
10+
11+
Source() -> Rewrite(templates=packets) -> arp -> Sink()

core/modules/arp_responder.cc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "arp_responder.h"
2+
3+
#include "../utils/arp.h"
4+
5+
using bess::utils::Arp;
6+
using bess::utils::be16_t;
7+
8+
const Commands ArpResponder::cmds = {
9+
{"add", "ArpResponderArg", MODULE_CMD_FUNC(&ArpResponder::CommandAdd), 0}};
10+
11+
CommandResponse ArpResponder::CommandAdd(const bess::pb::ArpResponderArg &arg) {
12+
be32_t ip_addr;
13+
arp_entry entry;
14+
15+
if (!arg.ip().length()) {
16+
return CommandFailure(EINVAL, "IP address is missing");
17+
}
18+
if (!bess::utils::ParseIpv4Address(arg.ip(), &ip_addr)) {
19+
return CommandFailure(EINVAL, "Invalid IP Address: %s", arg.ip().c_str());
20+
}
21+
22+
if (!entry.mac_addr.FromString(arg.mac_addr())) {
23+
return CommandFailure(EINVAL, "Invalid MAC Address: %s",
24+
arg.mac_addr().c_str());
25+
}
26+
27+
entry.ip_addr = ip_addr;
28+
entries_[ip_addr] = entry;
29+
return CommandSuccess();
30+
}
31+
32+
void ArpResponder::ProcessBatch(bess::PacketBatch *batch) {
33+
gate_idx_t out_gates[bess::PacketBatch::kMaxBurst];
34+
35+
int cnt = batch->cnt();
36+
for (int i = 0; i < cnt; i++) {
37+
bess::Packet *pkt = batch->pkts()[i];
38+
39+
out_gates[i] = 0;
40+
41+
Ethernet *eth = pkt->head_data<Ethernet *>();
42+
if (eth->ether_type != be16_t(Ethernet::Type::kArp)) {
43+
// Currently drop all non ARP packets, but can also just continue
44+
out_gates[i] = DROP_GATE;
45+
continue;
46+
}
47+
48+
Arp *arp = reinterpret_cast<Arp *>(eth + 1);
49+
if (arp->opcode == be16_t(Arp::Opcode::kRequest)) {
50+
// TODO(galsagie) When learn is added, learn SRC MAC here
51+
52+
// Try to find target IP in cache, if exists convert request to reply
53+
auto it = entries_.find(arp->target_ip_addr);
54+
if (it != entries_.end()) {
55+
const struct arp_entry &entry = it->second;
56+
arp->opcode = be16_t(Arp::Opcode::kReply);
57+
58+
eth->dst_addr = eth->src_addr;
59+
eth->src_addr = entry.mac_addr;
60+
61+
arp->target_hw_addr = arp->sender_hw_addr;
62+
arp->sender_hw_addr = entry.mac_addr;
63+
64+
arp->target_ip_addr = arp->sender_ip_addr;
65+
arp->sender_ip_addr = entry.ip_addr;
66+
} else {
67+
// Did not find an ARP entry in cache, drop packet
68+
// TODO(galsagie) Optinally continue packet to next module here
69+
out_gates[i] = DROP_GATE;
70+
}
71+
} else if (arp->opcode == be16_t(Arp::Opcode::kReply)) {
72+
// TODO(galsagie) When learn is added, learn SRC MAC here
73+
out_gates[i] = DROP_GATE;
74+
} else {
75+
// TODO(galsagie) Other opcodes are not handled yet.
76+
out_gates[i] = DROP_GATE;
77+
}
78+
}
79+
80+
RunSplit(out_gates, batch);
81+
}
82+
83+
ADD_MODULE(ArpResponder, "arp_responder",
84+
"Respond to ARP requests and learns new MAC's")

core/modules/arp_responder.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#ifndef BESS_MODULES_ARP_RESPONDER_H_
2+
#define BESS_MODULES_ARP_RESPONDER_H_
3+
4+
#include <map>
5+
6+
#include "../module.h"
7+
#include "../module_msg.pb.h"
8+
#include "../utils/endian.h"
9+
#include "../utils/ether.h"
10+
#include "../utils/ip.h"
11+
12+
using bess::utils::Ethernet;
13+
using bess::utils::be32_t;
14+
15+
// ARP cache entry struct which keeps mapping between IP and MAC
16+
struct arp_entry {
17+
Ethernet::Address mac_addr;
18+
be32_t ip_addr;
19+
uint64_t time; // timestamp used to expire cache entries (in milliseconds)
20+
};
21+
22+
// ARP Responder module
23+
// Answer ARP requests from an internal configurable cache
24+
// Currently drops non ARP packets
25+
class ArpResponder final : public Module {
26+
public:
27+
static const gate_idx_t kNumIGates = 1;
28+
static const gate_idx_t kNumOGates = 1;
29+
30+
static const Commands cmds;
31+
32+
void ProcessBatch(bess::PacketBatch *batch) override;
33+
34+
CommandResponse CommandAdd(const bess::pb::ArpResponderArg &arg);
35+
36+
private:
37+
// Mapping between IP (key) and its ARP entry (MAC Address)
38+
std::map<be32_t, struct arp_entry> entries_;
39+
};
40+
41+
#endif // BESS_MODULES_ARP_RESPONDER_H_

protobuf/module_msg.proto

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,3 +972,18 @@ message VXLANEncapArg {
972972
message WildcardMatchArg {
973973
repeated WildcardMatchField fields = 1; /// A list of WildcardMatch fields.
974974
}
975+
976+
/**
977+
* The ARP Responder module is responding to ARP requests
978+
* TODO: Dynamic learn new MAC's-IP's mapping
979+
*
980+
* __Input Gates__: 1
981+
* __Output Gates__: 1
982+
*/
983+
message ArpResponderArg {
984+
/**
985+
* One ARP IP-MAC mapping
986+
*/
987+
string ip = 1; // The IP
988+
string mac_addr = 2; /// The MAC address
989+
}

0 commit comments

Comments
 (0)