Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions bessctl/conf/samples/arp.bess
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scapy.all as scapy

eth_header = scapy.Ether(src='02:1e:67:9f:4d:ae', dst='ff:ff:ff:ff:ff:ff')
arp_header = scapy.ARP(op=1, pdst='1.2.3.4')
pkt = eth_header/arp_header
packets = [str(pkt)]

arp::ArpResponder()
arp.add(ip='1.2.3.4', mac_addr='A0:22:33:44:55:66')

Source() -> Rewrite(templates=packets) -> arp -> Sink()
84 changes: 84 additions & 0 deletions core/modules/arp_responder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "arp_responder.h"

#include "../utils/arp.h"

using bess::utils::Arp;
using bess::utils::be16_t;

const Commands ArpResponder::cmds = {
{"add", "ArpResponderArg", MODULE_CMD_FUNC(&ArpResponder::CommandAdd), 0}};

CommandResponse ArpResponder::CommandAdd(const bess::pb::ArpResponderArg &arg) {
be32_t ip_addr;
arp_entry entry;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to declare entry.

@GalSagie GalSagie Jun 1, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below


if (!arg.ip().length()) {
return CommandFailure(EINVAL, "IP address is missing");
}
if (!bess::utils::ParseIpv4Address(arg.ip(), &ip_addr)) {
return CommandFailure(EINVAL, "Invalid IP Address: %s", arg.ip().c_str());
}

if (!entry.mac_addr.FromString(arg.mac_addr())) {
return CommandFailure(EINVAL, "Invalid MAC Address: %s",
arg.mac_addr().c_str());
}

entry.ip_addr = ip_addr;
entries_[ip_addr] = entry;
return CommandSuccess();
}

void ArpResponder::ProcessBatch(bess::PacketBatch *batch) {
gate_idx_t out_gates[bess::PacketBatch::kMaxBurst];

int cnt = batch->cnt();
for (int i = 0; i < cnt; i++) {
bess::Packet *pkt = batch->pkts()[i];

out_gates[i] = 0;

Ethernet *eth = pkt->head_data<Ethernet *>();
if (eth->ether_type != be16_t(Ethernet::Type::kArp)) {
// Currently drop all non ARP packets, but can also just continue
out_gates[i] = DROP_GATE;
continue;
}

Arp *arp = reinterpret_cast<Arp *>(eth + 1);
if (arp->opcode == be16_t(Arp::Opcode::kRequest)) {
// TODO(galsagie) When learn is added, learn SRC MAC here

// Try to find target IP in cache, if exists convert request to reply
auto it = entries_.find(arp->target_ip_addr);
if (it != entries_.end()) {
const struct arp_entry &entry = it->second;
arp->opcode = be16_t(Arp::Opcode::kReply);

eth->dst_addr = eth->src_addr;
eth->src_addr = entry.mac_addr;

arp->target_hw_addr = arp->sender_hw_addr;
arp->sender_hw_addr = entry.mac_addr;

arp->target_ip_addr = arp->sender_ip_addr;
arp->sender_ip_addr = entry.ip_addr;
} else {
// Did not find an ARP entry in cache, drop packet
// TODO(galsagie) Optinally continue packet to next module here
out_gates[i] = DROP_GATE;
}
} else if (arp->opcode == be16_t(Arp::Opcode::kReply)) {
// TODO(galsagie) When learn is added, learn SRC MAC here
out_gates[i] = DROP_GATE;
} else {
// TODO(galsagie) Other opcodes are not handled yet.
out_gates[i] = DROP_GATE;
}
}

RunSplit(out_gates, batch);
}

ADD_MODULE(ArpResponder, "arp_responder",
"Respond to ARP requests and learns new MAC's")
41 changes: 41 additions & 0 deletions core/modules/arp_responder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef BESS_MODULES_ARP_RESPONDER_H_
#define BESS_MODULES_ARP_RESPONDER_H_

#include <map>

#include "../module.h"
#include "../module_msg.pb.h"
#include "../utils/endian.h"
#include "../utils/ether.h"
#include "../utils/ip.h"

using bess::utils::Ethernet;
using bess::utils::be32_t;

// ARP cache entry struct which keeps mapping between IP and MAC
struct arp_entry {
Ethernet::Address mac_addr;
be32_t ip_addr;
uint64_t time; // timestamp used to expire cache entries (in milliseconds)
};

// ARP Responder module
// Answer ARP requests from an internal configurable cache
// Currently drops non ARP packets
class ArpResponder final : public Module {
public:
static const gate_idx_t kNumIGates = 1;
static const gate_idx_t kNumOGates = 1;

static const Commands cmds;

void ProcessBatch(bess::PacketBatch *batch) override;

CommandResponse CommandAdd(const bess::pb::ArpResponderArg &arg);

private:
// Mapping between IP (key) and its ARP entry (MAC Address)
std::map<be32_t, struct arp_entry> entries_;
};

#endif // BESS_MODULES_ARP_RESPONDER_H_
15 changes: 15 additions & 0 deletions protobuf/module_msg.proto
Original file line number Diff line number Diff line change
Expand Up @@ -972,3 +972,18 @@ message VXLANEncapArg {
message WildcardMatchArg {
repeated WildcardMatchField fields = 1; /// A list of WildcardMatch fields.
}

/**
* The ARP Responder module is responding to ARP requests
* TODO: Dynamic learn new MAC's-IP's mapping
*
* __Input Gates__: 1
* __Output Gates__: 1
*/
message ArpResponderArg {
/**
* One ARP IP-MAC mapping
*/
string ip = 1; // The IP
string mac_addr = 2; /// The MAC address
}