import os import fnmatch import time from hook import Hook from collections import deque # Sphinx creates auto-generated documentation by importing the python source # files and collecting the docstrings from them. The NO_VPP_PAPI flag allows # the vpp_papi_provider.py file to be importable without having to build # the whole vpp api if the user only wishes to generate the test documentation. do_import = True try: no_vpp_papi = os.getenv("NO_VPP_PAPI") if no_vpp_papi == "1": do_import = False except: pass if do_import: from vpp_papi import VPP # from vnet/vnet/mpls/mpls_types.h MPLS_IETF_MAX_LABEL = 0xfffff MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1 class L2_VTR_OP: L2_DISABLED = 0 L2_PUSH_1 = 1 L2_PUSH_2 = 2 L2_POP_1 = 3 L2_POP_2 = 4 L2_TRANSLATE_1_1 = 5 L2_TRANSLATE_1_2 = 6 L2_TRANSLATE_2_1 = 7 L2_TRANSLATE_2_2 = 8 class QOS_SOURCE: EXT = 0 VLAN = 1 MPLS = 2 IP = 3 class UnexpectedApiReturnValueError(Exception): """ exception raised when the API return value is unexpected """ pass class VppPapiProvider(object): """VPP-api provider using vpp-papi @property hook: hook object providing before and after api/cli hooks """ _zero, _negative = range(2) def __init__(self, name, shm_prefix, test_class): self.hook = Hook("vpp-papi-provider") self.name = name self.shm_prefix = shm_prefix self.test_class = test_class self._expect_api_retval = self._zero self._expect_stack = [] jsonfiles = [] install_dir = os.getenv('VPP_TEST_INSTALL_PATH') for root, dirnames, filenames in os.walk(install_dir): for filename in fnmatch.filter(filenames, '*.api.json'): jsonfiles.append(os.path.join(root, filename)) self.vpp = VPP(jsonfiles, logger=test_class.logger) self._events = deque() def __enter__(self): return self def expect_negative_api_retval(self): """ Expect API failure """ self._expect_stack.append(self._expect_api_retval) self._expect_api_retval = self._negative return self def expect_zero_api_retval(self): """ Expect API success """ self._expect_stack.append(self._expect_api_retval) self._expect_api_retval = self._zero return self def __exit__(self, exc_type, exc_value, traceback): self._expect_api_retval = self._expect_stack.pop() def register_hook(self, hook): """Replace hook registration with new hook :param hook: """ self.hook = hook def collect_events(self): """ Collect all events from the internal queue and clear the queue. """ e = self._events self._events = deque() return e def wait_for_event(self, timeout, name=None): """ Wait for and return next event. """ if name: self.test_class.logger.debug("Expecting event '%s' within %ss", name, timeout) else: self.test_class.logger.debug("Expecting event within %ss", timeout) if self._events: self.test_class.logger.debug("Not waiting, event already queued") limit = time.time() + timeout while time.time() < limit: if self._events: e = self._events.popleft() if name and type(e).__name__ != name: raise Exception( "Unexpected event received: %s, expected: %s" % (type(e).__name__, name)) self.test_class.logger.debug("Returning event %s:%s" % (name, e)) return e time.sleep(0) # yield raise Exception("Event did not occur within timeout") def __call__(self, name, event): """ Enqueue event in the internal event queue. """ # FIXME use the name instead of relying on type(e).__name__ ? # FIXME #2 if this throws, it is eaten silently, Ole? self.test_class.logger.debug("New event: %s: %s" % (name, event)) self._events.append(event) def connect(self): """Connect the API to VPP""" self.vpp.connect(self.name, self.shm_prefix) self.papi = self.vpp.api self.vpp.register_event_callback(self) def disconnect(self): """Disconnect the API from VPP""" self.vpp.disconnect() def api(self, api_fn, api_args, expected_retval=0): """ Call API function and check it's return value. Call the appropriate hooks before and after the API call :param api_fn: API function to call :param api_args: tuple of API function arguments :param expected_retval: Expected return value (Default value = 0) :returns: reply from the API """ self.hook.before_api(api_fn.__name__, api_args) reply = api_fn(**api_args) if self._expect_api_retval == self._negative: if hasattr(reply, 'retval') and reply.retval >= 0: msg = "API call passed unexpectedly: expected negative "\ "return value instead of %d in %s" % \ (reply.retval, repr(reply)) self.test_class.logger.info(msg) raise UnexpectedApiReturnValueError(msg) elif self._expect_api_retval == self._zero: if hasattr(reply, 'retval') and reply.retval != expected_retval: msg = "API call failed, expected %d return value instead "\ "of %d in %s" % (expected_retval, reply.retval, repr(reply)) self.test_class.logger.info(msg) raise UnexpectedApiReturnValueError(msg) else: raise Exception("Internal error, unexpected value for " "self._expect_api_retval %s" % self._expect_api_retval) self.hook.after_api(api_fn.__name__, api_args) return reply def cli(self, cli): """ Execute a CLI, calling the before/after hooks appropriately. :param cli: CLI to execute :returns: CLI output """ self.hook.before_cli(cli) cli += '\n' r = self.papi.cli_inband(length=len(cli), cmd=cli) self.hook.after_cli(cli) if hasattr(r, 'reply'): return r.reply.decode().rstrip('\x00') def ppcli(self, cli): """ Helper method to print CLI command in case of info logging level. :param cli: CLI to execute :returns: CLI output """ return cli + "\n" + str(self.cli(cli)) def _convert_mac(self, mac): return mac.replace(':', '').decode('hex') def show_version(self): """ """ return self.api(self.papi.show_version, {}) def pg_create_interface(self, pg_index): """ :param pg_index: """ return self.api(self.papi.pg_create_interface, {"interface_id": pg_index}) def sw_interface_dump(self, filter=None): """ :param filter: (Default value = None) """ if filter is not None: args = {"name_filter_valid": 1, "name_filter": filter} else: args = {} return self.api(self.papi.sw_interface_dump, args) def sw_interface_set_table(self, sw_if_index, is_ipv6, table_id): """ Set the IPvX Table-id for the Interface :param sw_if_index: :param is_ipv6: :param table_id: """ return self.api(self.papi.sw_interface_set_table, {'sw_if_index': sw_if_index, 'is_ipv6': is_ipv6, 'vrf_id': table_id}) def sw_interface_add_del_address(self, sw_if_index, addr, addr_len, is_ipv6=0, is_add=1, del_all=0): """ :param addr: param is_ipv6: (Default value = 0) :param sw_if_index: :param addr_len: :param is_ipv6: (Default value = 0) :param is_add: (Default value = 1) :param del_all: (Default value = 0) """ return self.api(self.papi.sw_interface_add_del_address, {'sw_if_index': sw_if_index, 'is_add': is_add, 'is_ipv6': is_ipv6, 'del_all': del_all, 'address_length': addr_len, 'address': addr}) def sw_interface_set_unnumbered(self, sw_if_index, ip_sw_if_index, is_add=1): """ Set the Interface to be unnumbered :param is_add: (Default value = 1) :param sw_if_index - interface That will be unnumbered :param ip_sw_if_index - interface with an IP addres """ return self.api(self.papi.sw_interface_set_unnumbered, {'sw_if_index': ip_sw_if_index, 'unnumbered_sw_if_index': sw_if_index, 'is_add': is_add}) def sw_interface_enable_disable_mpls(self, sw_if_index, is_enable=1): """ Enable/Disable MPLS on the interface :param sw_if_index: :param is_enable: (Default value = 1) """ return self.api(self.papi.sw_interface_set_mpls_enable, {'sw_if_index': sw_if_index, 'enable': is_enable}) def sw_interface_ra_suppress(self, sw_if_index, suppress=1): return self.api(self.papi.sw_interface_ip6nd_ra_config, {'sw_if_index': sw_if_index, 'suppress': suppress}) def set_ip_flow_hash(self, table_id, src=1, dst=1, sport=1, dport=1, proto=1, reverse=0, is_ip6=0): return self.api(self.papi.set_ip_flow_hash, {'vrf_id': table_id, 'src': src, 'dst': dst, 'dport': dport, 'sport': sport, 'proto': proto, 'reverse': reverse, 'is_ipv6': is_ip6}) def ip6_nd_proxy(self, address, sw_if_index, is_del=0): return self.api(self.papi.ip6nd_proxy_add_del, {'address': address, 'sw_if_index': sw_if_index, 'is_del': is_del}) def ip6_sw_interface_ra_config(self, sw_if_index, no, suppress, send_unicast): return self.api(self.papi.sw_interface_ip6nd_ra_config, {'sw_if_index': sw_if_index, 'is_no': no, 'suppress': suppress, 'send_unicast': send_unicast}) def ip6_sw_interface_ra_prefix(self, sw_if_index, address, address_length, use_default=0, no_advertise=0, off_link=0, no_autoconfig=0, no_onlink=0, is_no=0, val_lifetime=0xffffffff, pref_lifetime=0xffffffff): return self.api(self.papi.sw_interface_ip6nd_ra_prefix, {'sw_if_index': sw_if_index, 'address': address, 'address_length': address_length, 'use_default': use_default, 'no_advertise': no_advertise, 'off_link': off_link, 'no_autoconfig': no_autoconfig, 'no_onlink': no_onlink, 'is_no': is_no, 'val_lifetime': val_lifetime, 'pref_lifetime': pref_lifetime}) def ip6_sw_interface_enable_disable(self, sw_if_index, enable): """ Enable/Disable An interface for IPv6 """ return self.api(self.papi.sw_interface_ip6_enable_disable, {'sw_if_index': sw_if_index, 'enable': enable}) def vxlan_add_del_tunnel( self, src_addr, dst_addr, mcast_sw_if_index=0xFFFFFFFF, is_add=1, is_ipv6=0, encap_vrf_id=0, decap_next_index=0xFFFFFFFF, vni=0, instance=0xFFFFFFFF): """ :param dst_addr: :param src_addr: :param is_add: (Default value = 1) :param is_ipv6: (Default value = 0) :param encap_vrf_id: (Default value = 0) :param decap_next_index: (Default value = 0xFFFFFFFF) :param mcast_sw_if_index: (Default value = 0xFFFFFFFF) :param vni: (Default value = 0) :param instance: (Default value = 0xFFFFFFFF) """ return self.api(self.papi.vxlan_add_del_tunnel, {'is_add': is_add, 'is_ipv6': is_ipv6, 'src_address': src_addr, 'dst_address': dst_addr, 'mcast_sw_if_index': mcast_sw_if_index, 'encap_vrf_id': encap_vrf_id, 'decap_next_index': decap_next_index, 'vni': vni, 'instance': instance}) def geneve_add_del_tunnel( self, local_addr, remote_addr, mcast_sw_if_index=0xFFFFFFFF, is_add=1, is_ipv6=0, encap_vrf_id=0, decap_next_index=0xFFFFFFFF, vni=0): """ :param remote_addr: :param local_addr: :param is_add: (Default value = 1) :param is_ipv6: (Default value = 0) :param encap_vrf_id: (Default value = 0) :param decap_next_index: (Default value = 0xFFFFFFFF) :param mcast_sw_if_index: (Default value = 0xFFFFFFFF) :param vni: (Default value = 0) """ return self.api(self.papi.geneve_add_del_tunnel, {'is_add': is_add, 'is_ipv6': is_ipv6, 'local_address': local_addr, 'remote_address': remote_addr, 'mcast_sw_if_index': mcast_sw_if_index, 'encap_vrf_id': encap_vrf_id, 'decap_next_index': decap_next_index, 'vni': vni}) def bridge_domain_add_del(self, bd_id, flood=1, uu_flood=1, forward=1, learn=1, arp_term=0, is_add=1): """Create/delete bridge domain. :param int bd_id: Bridge domain index. :param int flood: Enable/disable bcast/mcast flooding in the BD. (Default value = 1) :param int uu_flood: Enable/disable unknown unicast flood in the BD. (Default value = 1) :param int forward: Enable/disable forwarding on all interfaces in the BD. (Default value = 1) :param int learn: Enable/disable learning on all interfaces in the BD. (Default value = 1) :param int arp_term: Enable/disable arp termination in the BD. (Default value = 1) :param int is_add: Add or delete flag. (Default value = 1) """ return self.api(self.papi.bridge_domain_add_del, {'bd_id': bd_id, 'flood': flood, 'uu_flood': uu_flood, 'forward': forward, 'learn': learn, 'arp_term': arp_term, 'is_add': is_add}) def bd_ip_mac_add_del(self, bd_id, mac, ip, is_ipv6=0, is_add=1): return self.api(self.papi.bd_ip_mac_add_del, {'bd_id': bd_id, 'is_add': is_add, 'is_ipv6': is_ipv6, 'ip_address': ip, 'mac_address': mac}) def want_ip4_arp_events(self, enable_disable=1, address=0): return self.api(self.papi.want_ip4_arp_events, {'enable_disable': enable_disable, 'address': address, 'pid': os.getpid(), }) def want_ip6_nd_events(self, enable_disable=1, address=0): return self.api(self.papi.want_ip6_nd_events, {'enable_disable': enable_disable, 'address': address, 'pid': os.getpid(), }) def want_ip6_ra_events(self, enable_disable=1): return self.api(self.papi.want_ip6_ra_events, {'enable_disable': enable_di
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vnet/lisp-cp/lisp_msg_serdes.h>
#include <vnet/lisp-cp/packets.h>
#include <vppinfra/time.h>
void *lisp_msg_put_gid (vlib_buffer_t * b, gid_address_t * gid);
static void
lisp_msg_put_locators (vlib_buffer_t * b, locator_t * locators)
{
locator_t *loc;
vec_foreach (loc, locators)
{
u8 *p = vlib_buffer_put_uninit (b, sizeof (locator_hdr_t));
clib_memset (p, 0, sizeof (locator_hdr_t));
LOC_PRIORITY (p) = loc->priority;
LOC_MPRIORITY (p) = loc->mpriority;
LOC_WEIGHT (p) = loc->weight;
LOC_MWEIGHT (p) = loc->mweight;
LOC_LOCAL (p) = loc->local;
LOC_PROBED (p) = loc->probed ? 1 : 0;
LOC_REACHABLE (p) = loc->state ? 1 : 0;
lisp_msg_put_gid (b, &loc->address);
}
}
static void
lisp_msg_put_mapping_record (vlib_buffer_t * b, mapping_t * record)
{
mapping_record_hdr_t *p =
vlib_buffer_put_uninit (b, sizeof (mapping_record_hdr_t));
gid_address_t *eid = &record->eid;
clib_memset (p, 0, sizeof (*p));
MAP_REC_EID_PLEN (p) = gid_address_len (eid);
MAP_REC_TTL (p) = clib_host_to_net_u32 (MAP_REGISTER_DEFAULT_TTL);
MAP_REC_AUTH (p) = record->authoritative ? 1 : 0;
MAP_REC_LOC_COUNT (p) = vec_len (record->locators);
lisp_msg_put_gid (b, eid);
lisp_msg_put_locators (b, record->locators);
}
static void
lisp_msg_put_mreg_records (vlib_buffer_t * b, mapping_t * records)
{
u32 i;
for (i = 0; i < vec_len (records); i++)
lisp_msg_put_mapping_record (b, &records[i]);
}
void *
lisp_msg_put_gid (vlib_buffer_t * b, gid_address_t * gid)
{
u8 *p = 0;
if (!gid)
{
/* insert only src-eid-afi field set to 0 */
p = vlib_buffer_put_uninit (b, sizeof (u16));
*(u16 *) p = 0;
}
else
{
p = vlib_buffer_put_uninit (b, gid_address_size_to_put (gid));
gid_address_put (p, gid);
}
return p;
}
static void *
lisp_msg_put_itr_rlocs (lisp_cp_main_t * lcm, vlib_buffer_t * b,
gid_address_t * rlocs, u8 * locs_put)
{
u8 *bp, count = 0;
u32 i;
bp = vlib_buffer_get_current (b);
for (i = 0; i < vec_len (rlocs); i++)
{
lisp_msg_put_gid (b, &rlocs[i]);
count++;
}
*locs_put = count - 1;
return bp;
}
void *
lisp_msg_put_eid_rec (vlib_buffer_t * b, gid_address_t * eid)
{
eid_record_hdr_t *h = vlib_buffer_put_uninit (b, sizeof (*h));
clib_memset (h, 0, sizeof (*h));
EID_REC_MLEN (h) = gid_address_len (eid);
lisp_msg_put_gid (b, eid);
return h;
}
u64
nonce_build (u32 seed)
{
u64 nonce;
u32 nonce_lower;
u32 nonce_upper;
struct timespec ts;
/* Put nanosecond clock in lower 32-bits and put an XOR of the nanosecond
* clock with the second clock in the upper 32-bits. */
syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts);
nonce_lower = ts.tv_nsec;
nonce_upper = ts.tv_sec ^ clib_host_to_net_u32 (nonce_lower);
/* OR in a caller provided seed to the low-order 32-bits. */
nonce_lower |= seed;
/* Return 64-bit nonce. */
nonce = nonce_upper;
nonce = (nonce << 32) | nonce_lower;
return nonce;
}
void *
lisp_msg_put_map_reply (vlib_buffer_t * b, mapping_t * records, u64 nonce,
u8 probe_bit)
{
map_reply_hdr_t *h = vlib_buffer_put_uninit (b, sizeof (h[0]));
clib_memset (h, 0, sizeof (h[0]));
MREP_TYPE (h) = LISP_MAP_REPLY;
MREP_NONCE (h) = nonce;
MREP_REC_COUNT (h) = 1;
MREP_RLOC_PROBE (h) = probe_bit;
lisp_msg_put_mreg_records (b, records);
return h;
}
void *
lisp_msg_put_map_register (vlib_buffer_t * b, mapping_t * records,
u8 want_map_notify, u16 auth_data_len, u64 * nonce,
u32 * msg_len)
{
u8 *auth_data = 0;
/* Basic header init */
map_register_hdr_t *h = vlib_buffer_put_uninit (b, sizeof (h[0]));
clib_memset (h, 0, sizeof (h[0]));
MREG_TYPE (h) = LISP_MAP_REGISTER;
MREG_NONCE (h) = nonce_build (0);
MREG_WANT_MAP_NOTIFY (h) = want_map_notify ? 1 : 0;
MREG_REC_COUNT (h) = vec_len (records);
auth_data = vlib_buffer_put_uninit (b, auth_data_len);
clib_memset (auth_data, 0, auth_data_len);
/* Put map register records */
lisp_msg_put_mreg_records (b, records);
nonce[0] = MREG_NONCE (h);
msg_len[0] = vlib_buffer_get_tail (b) - (u8 *) h;
return h;
}
void *
lisp_msg_put_mreq (lisp_cp_main_t * lcm, vlib_buffer_t * b,
gid_address_t * seid, gid_address_t * deid,
gid_address_t * rlocs, u8 is_smr_invoked,
u8 rloc_probe_set, u64 * nonce)
{
u8 loc_count = 0;
/* Basic header init */
map_request_hdr_t *h = vlib_buffer_put_uninit (b, sizeof (h[0]));
clib_memset (h, 0, sizeof (h[0]));
MREQ_TYPE (h) = LISP_MAP_REQUEST;
MREQ_NONCE (h) = nonce_build (0);
MREQ_SMR_INVOKED (h) = is_smr_invoked ? 1 : 0;
MREQ_RLOC_PROBE (h) = rloc_probe_set ? 1 : 0;
/* We're adding one eid record */
increment_record_count (h);
/* Fill source eid */
lisp_msg_put_gid (b, seid);
/* Put itr rlocs */
lisp_msg_put_itr_rlocs (lcm, b, rlocs, &loc_count);
MREQ_ITR_RLOC_COUNT (h) = loc_count;
/* Put eid record */
lisp_msg_put_eid_rec (b, deid);
nonce[0] = MREQ_NONCE (h);
return h;
}
void *
lisp_msg_push_ecm (vlib_main_t * vm, vlib_buffer_t * b, int lp, int rp,
gid_address_t * la, gid_address_t * ra)
{
ecm_hdr_t *h;
ip_address_t _src_ip, *src_ip = &_src_ip, _dst_ip, *dst_ip = &_dst_ip;
if (gid_address_type (la) != GID_ADDR_IP_PREFIX)
{
/* empty ip4 */
clib_memset (src_ip, 0, sizeof (src_ip[0]));
clib_memset (dst_ip, 0, sizeof (dst_ip[0]));
}
else
{
src_ip = &gid_address_ip (la);
dst_ip = &gid_address_ip (ra);
}
/* Push inner ip and udp */
pkt_push_udp_and_ip (vm, b, lp, rp, src_ip, dst_ip, 0);
/* Push lisp ecm hdr */
h = pkt_push_ecm_hdr (b);
return h;
}
static u32
msg_type_to_hdr_len (lisp_msg_type_e type)
{
switch (type)
{
case LISP_MAP_REQUEST:
return (sizeof (map_request_hdr_t));
case LISP_MAP_REPLY:
return (sizeof (map_reply_hdr_t));
default:
return (0);
}
}
void *
lisp_msg_pull_hdr (vlib_buffer_t * b, lisp_msg_type_e type)
{
return vlib_buffer_pull (b, msg_type_to_hdr_len (type));
}
u32
lisp_msg_parse_addr (vlib_buffer_t * b, gid_address_t * eid)
{
u32 len;
clib_memset (eid, 0, sizeof (*eid));
len = gid_address_parse (vlib_buffer_get_current (b), eid);
if (len != ~0)
vlib_buffer_pull (b, len);
return len;
}
u32
lisp_msg_parse_eid_rec (vlib_buffer_t * b, gid_address_t * eid)
{
eid_record_hdr_t *h = vlib_buffer_get_current (b);
u32 len;
clib_memset (eid, 0, sizeof (*eid));
len = gid_address_parse (EID_REC_ADDR (h), eid);
if (len == ~0)
return len;
gid_address_ippref_len (eid) = EID_REC_MLEN (h);
vlib_buffer_pull (b, len + sizeof (eid_record_hdr_t));
return len + sizeof (eid_record_hdr_t);
}
u32
lisp_msg_parse_itr_rlocs (vlib_buffer_t * b, gid_address_t ** rlocs,
u8 rloc_count)
{
gid_address_t tloc;
u32 i, len = 0, tlen = 0;
//MREQ_ITR_RLOC_COUNT(mreq_hdr) + 1
for (i = 0; i < rloc_count; i++)
{
len = lisp_msg_parse_addr (b, &tloc);
if (len == ~0)
return len;
vec_add1 (*rlocs, tloc);
tlen += len;
}
return tlen;
}
u32
lisp_msg_parse_loc (vlib_buffer_t * b, locator_t * loc)
{
int len;
len = locator_parse (vlib_buffer_get_current (b), loc);
if (len == ~0)
return ~0;
if (!vlib_buffer_has_space (b, sizeof (len)))
return ~0;
vlib_buffer_pull (b, len);
return len;
}
u32
lisp_msg_parse_mapping_record (vlib_buffer_t * b, gid_address_t * eid,
locator_t ** locs, locator_t * probed_)
{
void *h = 0, *loc_hdr = 0;
locator_t loc, *probed = 0;
int i = 0, len = 0, llen = 0;
h = vlib_buffer_get_current (b);
if (!vlib_buffer_has_space (b, sizeof (mapping_record_hdr_t)))
return ~0;
vlib_buffer_pull (b, sizeof (mapping_record_hdr_t));
clib_memset (eid, 0, sizeof (*eid));
len = gid_address_parse (vlib_buffer_get_current (b), eid);
if (len == ~0)
return len;
if (!vlib_buffer_has_space (b, sizeof (len)))
return ~0;
vlib_buffer_pull (b, len);
if (GID_ADDR_IP_PREFIX == gid_address_type (eid))
gid_address_ippref_len (eid) = MAP_REC_EID_PLEN (h);
for (i = 0; i < MAP_REC_LOC_COUNT (h); i++)
{
loc_hdr = vlib_buffer_get_current (b);
llen = lisp_msg_parse_loc (b, &loc);
if (llen == ~0)
return llen;
vec_add1 (*locs, loc);
len += llen;
if (LOC_PROBED (loc_hdr))
{
if (probed != 0)
clib_warning
("Multiple locators probed! Probing only the first!");
else
probed = &loc;
}
}
/* XXX */
if (probed_ != 0 && probed)
*probed_ = *probed;
return len + sizeof (map_reply_hdr_t);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/