OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
arp_handler.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3  */
4 
5 #include <stdint.h>
6 #include <math.h>
7 #include "vr_defs.h"
8 #include "init/agent_param.h"
9 #include "oper/route_common.h"
10 #include "oper/operdb_init.h"
11 #include "oper/path_preference.h"
12 #include "pkt/pkt_init.h"
13 #include "services/arp_proto.h"
14 #include "services/services_init.h"
17 
18 ArpHandler::ArpHandler(Agent *agent, boost::shared_ptr<PktInfo> info,
19  boost::asio::io_context &io)
20  : ProtoHandler(agent, info, io), arp_(NULL), arp_tpa_(0) {
21  refcount_ = 0;
22 }
23 
25 }
26 
28 
29  // Process ARP only when the IP Fabric interface is configured
30 
31  assert(agent());
32  assert(agent()->GetArpProto());
33  ArpProto *arp_proto = agent()->GetArpProto();
34 
35  if (agent()->GetArpProto()->ip_fabric_interface() == NULL) {
36  arp_proto->IncrementStatsIPFabricNotInst();
37  delete pkt_info_->ipc;
38  return true;
39  }
40 
41  switch(pkt_info_->type) {
42  case PktType::MESSAGE:
43  return HandleMessage();
44 
45  default:
46  return HandlePacket();
47  }
48 }
49 
51  ArpProto *arp_proto = agent()->GetArpProto();
52  uint16_t arp_cmd;
53  if (pkt_info_->ip) {
54  arp_tpa_ = ntohl(pkt_info_->ip->ip_dst.s_addr);
55  arp_cmd = ARPOP_REQUEST;
56  } else if (pkt_info_->arp) {
57  arp_ = pkt_info_->arp;
58  if ((ntohs(arp_->ea_hdr.ar_hrd) != ARPHRD_ETHER) ||
59  (ntohs(arp_->ea_hdr.ar_pro) != ETHERTYPE_IP) ||
60  (arp_->ea_hdr.ar_hln != ETHER_ADDR_LEN) ||
61  (arp_->ea_hdr.ar_pln != IPv4_ALEN)) {
62  arp_proto->IncrementStatsInvalidPackets();
63  ARP_TRACE(Error, "Received Invalid ARP packet");
64  return true;
65  }
66  arp_cmd = ntohs(arp_->ea_hdr.ar_op);
67  union {
68  uint8_t data[sizeof(in_addr_t)];
69  in_addr_t addr;
70  } bytes;
71  memcpy(bytes.data, arp_->arp_tpa, sizeof(in_addr_t));
72  in_addr_t tpa = ntohl(bytes.addr);
73  memcpy(bytes.data, arp_->arp_spa, sizeof(in_addr_t));
74  in_addr_t spa = ntohl(bytes.addr);
75  if (arp_cmd == ARPOP_REQUEST)
76  arp_tpa_ = tpa;
77  else
78  arp_tpa_ = spa;
79 
80  // if it is our own, ignore
81  if (arp_tpa_ == agent()->router_id().to_ulong()) {
82  arp_proto->IncrementStatsGratuitous();
83  return true;
84  }
85 
86  if (tpa == spa || spa == 0) {
87  arp_cmd = GRATUITOUS_ARP;
88  }
89  } else {
90  arp_proto->IncrementStatsInvalidPackets();
91  ARP_TRACE(Error, "ARP : Received Invalid packet");
92  return true;
93  }
94 
95  const Interface *itf =
97  if (!itf || !itf->IsActive()) {
98  arp_proto->IncrementStatsInvalidInterface();
99  ARP_TRACE(Error, "Received ARP packet from invalid / inactive interface");
100  return true;
101  }
102 
103  const VrfEntry *vrf = agent()->vrf_table()->FindVrfFromId(pkt_info_->vrf);
104  if (!vrf || !vrf->IsActive()) {
105  arp_proto->IncrementStatsInvalidVrf();
106  ARP_TRACE(Error, "ARP : AgentHdr " + itf->name() +
107  " has no / inactive VRF ");
108  return true;
109  }
110  const VrfEntry *nh_vrf = itf->vrf();
111  if (!nh_vrf || !nh_vrf->IsActive()) {
112  arp_proto->IncrementStatsInvalidVrf();
113  ARP_TRACE(Error, "ARP : Interface " + itf->name() +
114  " has no / inactive VRF");
115  return true;
116  }
117 
118  // if broadcast ip, return
119  Ip4Address arp_addr(arp_tpa_);
120  if (arp_tpa_ == 0xFFFFFFFF || !arp_tpa_) {
121  arp_proto->IncrementStatsInvalidAddress();
122  ARP_TRACE(Error, "ARP : ignoring broadcast address" +
123  arp_addr.to_string());
124  return true;
125  }
126 
127  //Look for subnet broadcast
128  AgentRoute *route =
129  static_cast<InetUnicastAgentRouteTable *>(vrf->
130  GetInet4UnicastRouteTable())->FindLPM(arp_addr);
131  if (route) {
132  const NextHop *anh = route->GetActiveNextHop();
133  if (route->is_multicast()) {
134  arp_proto->IncrementStatsInvalidAddress();
135  ARP_TRACE(Error, "ARP : ignoring multicast address" +
136  arp_addr.to_string());
137  return true;
138  }
139  if (anh == NULL) {
140  arp_proto->IncrementStatsInvalidAddress();
141  ARP_TRACE(Error, "ARP : no active nexthop" +
142  arp_addr.to_string());
143  return true;
144  }
145  if(anh->GetType() == NextHop::RESOLVE) {
146  const ResolveNH *nh =
147  static_cast<const ResolveNH *>(anh);
148  itf = nh->get_interface();
149  nh_vrf = itf->vrf();
150  }
151  }
152 
153  ArpKey key(arp_tpa_, vrf);
154  ArpEntry *entry = arp_proto->FindArpEntry(key);
155 
156  if (nh_vrf->forwarding_vrf()) {
157  nh_vrf = nh_vrf->forwarding_vrf();
158  }
159 
160  switch (arp_cmd) {
161  case ARPOP_REQUEST: {
162  arp_proto->IncrementStatsArpReq();
163  arp_proto->IncrementStatsArpRequest(itf->id());
164  if (entry) {
165  entry->HandleArpRequest();
166  return true;
167  } else {
168  entry = new ArpEntry(io_, this, key, nh_vrf, ArpEntry::INITING,
169  itf);
170  if (arp_proto->AddArpEntry(entry) == false) {
171  delete entry;
172  return true;
173  }
174  entry->HandleArpRequest();
175  return false;
176  }
177  }
178 
179  case ARPOP_REPLY: {
180  arp_proto->IncrementStatsArpReplies();
181  arp_proto->IncrementStatsArpReply(itf->id());
182  if (itf->type() == Interface::VM_INTERFACE) {
183  uint32_t ip;
184  memcpy(&ip, arp_->arp_spa, sizeof(ip));
185  ip = ntohl(ip);
186  /* Enqueue a request to trigger state machine. The prefix-len
187  * of 32 passed below is not used. We do LPMFind on IP to
188  * figure out the actual prefix-len inside
189  * EnqueueTrafficSeen
190  */
192  EnqueueTrafficSeen(Ip4Address(ip), 32, itf->id(),
193  vrf->vrf_id(),
194  MacAddress(arp_->arp_sha));
195  arp_proto->HandlePathPreferenceArpReply(vrf, itf->id(),
196  Ip4Address(ip));
197 
198  if(entry) {
199  entry->HandleArpReply(MacAddress(arp_->arp_sha));
200  }
201  return true;
202  }
203  if(entry) {
204  entry->HandleArpReply(MacAddress(arp_->arp_sha));
205  return true;
206  } else {
207  entry = new ArpEntry(io_, this, key, nh_vrf, ArpEntry::INITING,
208  itf);
209  if (arp_proto->AddArpEntry(entry) == false) {
210  delete entry;
211  return true;
212  }
213  entry->HandleArpReply(MacAddress(arp_->arp_sha));
214  arp_ = NULL;
215  return false;
216  }
217  }
218 
219  case GRATUITOUS_ARP: {
220  arp_proto->IncrementStatsGratuitous();
221  if (itf->type() == Interface::VM_INTERFACE) {
222  uint32_t ip;
223  memcpy(&ip, arp_->arp_spa, sizeof(ip));
224  ip = ntohl(ip);
225  //Enqueue a request to trigger state machine
227  EnqueueTrafficSeen(Ip4Address(ip), 32, itf->id(),
228  vrf->vrf_id(),
229  MacAddress(arp_->arp_sha));
230  return true;
231  } else if (entry) {
232  entry->HandleArpReply(MacAddress(arp_->arp_sha));
233  return true;
234  } else {
235  // ignore gratuitous ARP when entry is not present in cache
236  return true;
237  }
238  }
239 
240  default:
241  ARP_TRACE(Error, "Received Invalid ARP command : " +
242  integerToString(arp_cmd));
243  return true;
244  }
245 }
246 
247 /* This API is invoked from the following paths
248  - NextHop notification for ARP_NH
249  - ARP Timer expiry
250  - Sending Gratituous ARP for Receive Nexthops
251  In all these above paths we expect route_vrf and nh_vrf for ArpRoute to
252  be same
253  */
255  bool ret = true;
256  ArpProto::ArpIpc *ipc = static_cast<ArpProto::ArpIpc *>(pkt_info_->ipc);
257  ArpProto *arp_proto = agent()->GetArpProto();
258  switch(pkt_info_->ipc->cmd) {
259  case ArpProto::ARP_RESOLVE: {
260  ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
261  if (!entry) {
262  entry = new ArpEntry(io_, this, ipc->key, ipc->key.vrf,
263  ArpEntry::INITING, ipc->interface_.get());
264  if (arp_proto->AddArpEntry(entry) == false) {
265  delete entry;
266  break;
267  }
268  ret = false;
269  }
270  arp_proto->IncrementStatsArpReq();
271  arp_proto->IncrementStatsArpRequest(ipc->interface_->id());
272  entry->HandleArpRequest();
273  break;
274  }
275 
277  bool key_valid = false;
279  arp_proto->GratuitousArpEntryIterator(ipc->key, &key_valid);
280  if (key_valid && !ipc->interface_->IsDeleted()) {
281  ArpEntry *entry = NULL;
282  ArpProto::ArpEntrySet::iterator sit = it->second.begin();
283  for (; sit != it->second.end(); sit++) {
284  entry = *sit;
285  if (entry->get_interface() == ipc->interface_.get())
286  break;
287  }
288  if (sit == it->second.end()) {
289  entry = new ArpEntry(io_, this, ipc->key, ipc->key.vrf,
290  ArpEntry::ACTIVE, ipc->interface_.get());
291  it->second.insert(entry);
292  ret = false;
293  }
294  if (entry)
295  entry->SendGratuitousArp();
296  break;
297  }
298  }
299 
300  case ArpProto::ARP_DELETE: {
301  EntryDelete(ipc->key);
302  break;
303  }
304 
306  ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
307  if (entry && !entry->RetryExpiry()) {
308  arp_proto->DeleteArpEntry(entry);
309  }
310  break;
311  }
312 
314  ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
315  if (entry && !entry->AgingExpiry()) {
316  arp_proto->DeleteArpEntry(entry);
317  }
318  break;
319  }
320 
322  ArpEntry *entry =
323  arp_proto->GratuitousArpEntry(ipc->key, ipc->interface_.get());
324  if (entry && entry->retry_count() <= ArpProto::kGratRetries) {
325  entry->SendGratuitousArp();
326  } else {
327  // Need to validate deleting the Arp entry upon fabric vrf Delete only
328  if (ipc->key.vrf->GetName() != agent()->fabric_vrf_name()) {
329  arp_proto->DeleteGratuitousArpEntry(entry);
330  }
331  }
332  break;
333  }
334 
335  default:
336  ARP_TRACE(Error, "Received Invalid internal ARP message : " +
337  integerToString(pkt_info_->ipc->cmd));
338  break;
339  }
340  delete ipc;
341  return ret;
342 }
343 
345  ArpProto *arp_proto = agent()->GetArpProto();
346  ArpEntry *entry = arp_proto->FindArpEntry(key);
347  if (entry) {
348  arp_proto->DeleteArpEntry(entry);
349  // this request comes when ARP NH is deleted; nothing more to do
350  }
351 }
352 
353 uint16_t ArpHandler::ArpHdr(const MacAddress &smac, in_addr_t sip,
354  const MacAddress &tmac, in_addr_t tip, uint16_t op) {
355  arp_->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
356  arp_->ea_hdr.ar_pro = htons(0x800);
357  arp_->ea_hdr.ar_hln = ETHER_ADDR_LEN;
358  arp_->ea_hdr.ar_pln = IPv4_ALEN;
359  arp_->ea_hdr.ar_op = htons(op);
360  smac.ToArray(arp_->arp_sha, sizeof(arp_->arp_sha));
361  sip = htonl(sip);
362  memcpy(arp_->arp_spa, &sip, sizeof(in_addr_t));
363  tmac.ToArray(arp_->arp_tha, sizeof(arp_->arp_tha));
364  tip = htonl(tip);
365  memcpy(arp_->arp_tpa, &tip, sizeof(in_addr_t));
366  return sizeof(ether_arp);
367 }
368 
369 void ArpHandler::SendArp(uint16_t op, const MacAddress &smac, in_addr_t sip,
370  const MacAddress &tmac, const MacAddress &dmac,
371  in_addr_t tip, uint32_t itf, uint32_t vrf) {
372 
373  if (pkt_info_->packet_buffer() == NULL) {
374  pkt_info_->AllocPacketBuffer(agent(), PktHandler::ARP, ARP_TX_BUFF_LEN,
375  0);
376  }
377 
378  char *buf = (char *)pkt_info_->packet_buffer()->data();
379  memset(buf, 0, pkt_info_->packet_buffer()->data_len());
380  pkt_info_->eth = (struct ether_header *)buf;
381  int l2_len = EthHdr(buf, pkt_info_->packet_buffer()->data_len(),
382  itf, smac, dmac, ETHERTYPE_ARP);
383  arp_ = pkt_info_->arp = (ether_arp *) (buf + l2_len);
384  arp_tpa_ = tip;
385 
386  ArpHdr(smac, sip, tmac, tip, op);
387  pkt_info_->set_len(l2_len + sizeof(ether_arp));
388 
390 }
391 
392 void ArpHandler::SendArpRequestByPlen(const VmInterface *vm_interface, const MacAddress &smac,
393  const ArpPathPreferenceState *data,
394  const Ip4Address &tpa) {
395  Ip4Address service_ip = vm_interface->GetServiceIp(data->ip()).to_v4();
396  bool aap_ip = vm_interface->MatchAapIp(data->ip(), data->plen());
397 #if 0
398  MacAddress mac = agent()->mac_learning_proto()->
399  GetMacIpLearningTable()->GetPairedMacAddress(
400  vm_interface->vrf()->vrf_id(),
401  data->ip());
402 #endif
403  MacAddress mac = data->mac();
404 
405  if (mac == MacAddress()) {
406  mac = vm_interface->vm_mac();
407  }
408 
409  if (data->plen() == Address::kMaxV4PrefixLen) {
410  SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(), MacAddress(),
411  ((aap_ip) ? MacAddress::BroadcastMac(): mac),
412  data->ip().to_v4().to_ulong(), vm_interface->id(), data->vrf_id());
414  } else {
415  if (!tpa.is_unspecified()) {
416  SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(),
417  MacAddress(), ((aap_ip) ? MacAddress::BroadcastMac(): mac),
418  tpa.to_ulong(), vm_interface->id(), data->vrf_id());
420  return;
421  }
422  /* Loop through all the IPs for the prefix-len and send Arp for each
423  * IP*/
424  uint8_t diff_plen = Address::kMaxV4PrefixLen - data->plen();
425  uint32_t num_addresses = pow(2, diff_plen);
426  const uint32_t &max_addresses = MaxArpProbeAddresses();
427  if (num_addresses > max_addresses) {
428  num_addresses = max_addresses;
429  /* When min_aap_prefix_len (configured by user in agent config file)
430  * we need to visit all addresses specified by user, because
431  * base address in this case will be formed by prefix-len lower
432  * than min_aap_prefix_len. This prefix len is determined by
433  * data->plen(). The loop below which goes through all the
434  * addresses will visit 2 addresses less after discounting base
435  * address and broadcast address. Because base address in this case
436  * is formed by prefix-len lower than min_aap_prefix_len, we should
437  * not discount the two addresses. Hence increment by 2.
438  */
439  num_addresses += 2;
440  }
441  uint32_t base_addr = data->ip().to_v4().to_ulong();
442  for (uint32_t i = 1; i < num_addresses; ++i) {
443  uint32_t addr = base_addr + i;
444  SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(),
445  MacAddress(), ((aap_ip) ? MacAddress::BroadcastMac(): mac),
446  addr, vm_interface->id(), data->vrf_id());
448  }
449  }
450 }
451 
453  uint32_t diff_plen = Address::kMaxV4PrefixLen -
455  return pow(2, diff_plen);
456 }
457 
459  p->refcount_++;
460 }
461 
463  if (p->refcount_.fetch_and_decrement() == 1) {
464  delete p;
465  }
466 }
void EntryDelete(ArpKey &key)
Definition: arp_handler.cc:344
int intrusive_ptr_add_ref(const AsPath *cpath)
Definition: bgp_aspath.h:147
const MacAddress & vm_mac() const
void HandleArpReply(const MacAddress &)
Definition: arp_entry.cc:68
Type type() const
Definition: interface.h:112
in_addr_t arp_tpa_
Definition: arp_handler.h:43
uint32_t vrf_id() const
Definition: arp_proto.h:275
Definition: vrf.h:86
bool ToArray(u_int8_t *p, size_t s) const
Definition: mac_address.cc:93
int retry_count() const
Definition: arp_entry.h:51
const uint32_t id() const
Definition: interface.h:123
bool AddArpEntry(ArpEntry *entry)
Definition: arp_proto.cc:838
MacLearningProto * mac_learning_proto() const
Definition: agent.h:1005
bool AgingExpiry()
Definition: arp_entry.cc:122
bool is_multicast() const
Definition: agent_route.h:274
void IncrementStatsArpReply(uint32_t idx)
Definition: arp_proto.cc:709
InterfaceTable * interface_table() const
Definition: agent.h:465
static const uint8_t kMaxV4PrefixLen
Definition: address.h:20
void IncrementStatsArpReq()
Definition: arp_proto.h:127
const string & GetName() const
Definition: vrf.h:100
IpAddress GetServiceIp(const IpAddress &ip) const
ArpProto * GetArpProto() const
Definition: agent.h:978
uint16_t min_aap_prefix_len() const
Definition: agent_param.h:460
VrfEntry * vrf() const
Definition: interface.h:115
Agent * agent() const
Definition: proto_handler.h:80
Type GetType() const
Definition: nexthop.h:405
Base class for all Route entries in agent.
Definition: agent_route.h:224
boost::shared_ptr< PktInfo > pkt_info_
Definition: proto_handler.h:92
void SendArpRequestByPlen(const VmInterface *vm_interface, const MacAddress &smac, const ArpPathPreferenceState *data, const Ip4Address &tpa)
Definition: arp_handler.cc:392
uint16_t ArpHdr(const MacAddress &smac, in_addr_t sip, const MacAddress &tmac, in_addr_t tip, uint16_t op)
Definition: arp_handler.cc:353
bool RetryExpiry()
Definition: arp_entry.cc:90
OperDB * oper_db() const
Definition: agent.cc:1013
uint32_t MaxArpProbeAddresses() const
Definition: arp_handler.cc:452
tbb::atomic< uint32_t > refcount_
Definition: arp_handler.h:44
bool HandlePacket()
Definition: arp_handler.cc:50
const std::string & fabric_vrf_name() const
Definition: agent.h:903
void IncrementStatsInvalidPackets()
Definition: arp_proto.h:135
int EthHdr(const MacAddress &src, const MacAddress &dest, const uint16_t proto)
VrfEntry * FindVrfFromId(size_t index)
Definition: vrf.cc:884
std::map< ArpKey, ArpEntrySet >::iterator GratuitousArpIterator
Definition: arp_proto.h:35
static const std::string integerToString(const NumberType &num)
Definition: string_util.h:19
Definition: agent.h:358
uint32_t GetInterfaceIndex() const
Definition: proto_handler.h:82
PathPreferenceModule * route_preference_module() const
Definition: operdb_init.h:58
void IncrementStatsIPFabricNotInst()
Definition: arp_proto.h:152
const NextHop * GetActiveNextHop() const
Definition: agent_route.cc:881
#define IPv4_ALEN
Definition: pkt_handler.h:40
void Send(uint32_t itf, uint32_t vrf, uint16_t, PktHandler::PktModuleName)
bool DeleteArpEntry(ArpEntry *entry)
Definition: arp_proto.cc:864
boost::asio::io_context & io_
Definition: proto_handler.h:93
InterfaceConstRef interface_
Definition: arp_proto.h:54
ArpEntry * FindArpEntry(const ArpKey &key)
Definition: arp_proto.cc:885
MacAddress mac(void) const
Definition: arp_proto.h:274
static const MacAddress & BroadcastMac()
Definition: mac_address.h:152
ArpHandler(Agent *agent, boost::shared_ptr< PktInfo > info, boost::asio::io_context &io)
Definition: arp_handler.cc:18
AgentParam * params() const
Definition: agent.h:1218
const Interface * get_interface() const
Definition: arp_entry.h:39
const uint32_t vrf_id() const
Definition: vrf.h:99
#define GRATUITOUS_ARP
Definition: arp_handler.h:10
boost::asio::ip::address_v4 Ip4Address
Definition: address.h:14
uint8_t plen() const
Definition: arp_proto.h:272
#define ARP_TX_BUFF_LEN
Definition: pkt_handler.h:41
void IncrementStatsGratuitous()
Definition: arp_proto.h:129
VrfTable * vrf_table() const
Definition: agent.h:485
void DeleteGratuitousArpEntry(ArpEntry *entry)
Definition: arp_proto.cc:776
static const uint16_t kGratRetries
Definition: arp_proto.h:21
#define ARP_TRACE(obj,...)
Definition: arp_proto.h:12
virtual ~ArpHandler()
Definition: arp_handler.cc:24
void IncrementStatsInvalidInterface()
Definition: arp_proto.h:139
const IpAddress & ip() const
Definition: arp_proto.h:271
void IncrementStatsArpReplies()
Definition: arp_proto.h:128
void IncrementStatsInvalidVrf()
Definition: arp_proto.h:143
bool HandleArpRequest()
Definition: arp_entry.cc:51
const Interface * FindInterface(size_t index) const
Definition: interface.cc:323
VrfEntry * forwarding_vrf() const
Definition: vrf.h:217
bool IsActive() const
Definition: agent_db.cc:27
void intrusive_ptr_release(const AsPath *cpath)
Definition: bgp_aspath.h:155
void IncrementStatsArpRequest(uint32_t idx)
Definition: arp_proto.cc:704
void SendGratuitousArp()
Definition: arp_entry.cc:137
const std::string & name() const
Definition: interface.h:114
const Interface * get_interface() const
Definition: nexthop.h:754
void SendArp(uint16_t op, const MacAddress &smac, in_addr_t sip, const MacAddress &tmac, const MacAddress &dmac, in_addr_t tip, uint32_t itf, uint32_t vrf)
Definition: arp_handler.cc:369
ArpProto::GratuitousArpIterator GratuitousArpEntryIterator(const ArpKey &key, bool *key_valid)
Definition: arp_proto.cc:809
const VrfEntry * vrf
Definition: arp_entry.h:18
bool Run()
Definition: arp_handler.cc:27
bool HandleMessage()
Definition: arp_handler.cc:254
bool MatchAapIp(const IpAddress &ip, uint8_t plen) const
ArpEntry * GratuitousArpEntry(const ArpKey &key, const Interface *intf)
Definition: arp_proto.cc:793
void HandlePathPreferenceArpReply(const VrfEntry *vrf, uint32_t itf, Ip4Address sip)
Definition: arp_proto.cc:954
void IncrementStatsVmArpReq()
Definition: arp_proto.h:134
ether_arp * arp_
Definition: arp_handler.h:42
void IncrementStatsInvalidAddress()
Definition: arp_proto.h:147