/* Dump common header. */ static void bgp_dump_header (struct stream *obuf, int type, int subtype, int dump_type) { struct timeval clock; long msecs; time_t secs; if ((dump_type == BGP_DUMP_ALL_ET || dump_type == BGP_DUMP_UPDATES_ET) && type == MSG_PROTOCOL_BGP4MP) type = MSG_PROTOCOL_BGP4MP_ET; gettimeofday(&clock, NULL); secs = clock.tv_sec; msecs = clock.tv_usec; /* Put dump packet header. */ stream_putl (obuf, secs); stream_putw (obuf, type); stream_putw (obuf, subtype); stream_putl (obuf, 0); /* len */ /* Adding microseconds for the MRT Extended Header */ if (type == MSG_PROTOCOL_BGP4MP_ET) stream_putl (obuf, msecs); }
void clear_checksum_streams (uint16_t checksum) { struct listnode * node, * nnode; struct sisis_listener * listener; for(ALL_LIST_ELEMENTS (sm->listen_sockets, node, nnode, listener)) { if(stream_get_getp(listener->chksum_stream) < stream_get_endp(listener->chksum_stream)) { uint16_t checksum_head = stream_getw(listener->chksum_stream); if(checksum_head != checksum) { stream_putw(listener->chksum_stream, checksum_head); uint16_t next_checksum = stream_peekw(listener->chksum_stream); while(next_checksum != checksum_head) { next_checksum = stream_getw(listener->chksum_stream); if(next_checksum != checksum) stream_putw(listener->chksum_stream, next_checksum); } } } } }
/* Dump BGP status change. */ void bgp_dump_state (struct peer *peer, int status_old, int status_new) { int ret; struct stream *obuf; /* If dump file pointer is disabled return immediately. */ if (bgp_dump_all.fp == NULL) return; /* Make dump stream. */ obuf = bgp_dump_obuf; stream_reset (obuf); bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4); bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/ stream_putw (obuf, status_old); stream_putw (obuf, status_new); /* Set length. */ bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); /* Write to the stream. */ ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); if (ret != 1) { zlog_warn ("bgp_dump_state: fwrite returned %d, expected 1: %s", ret, strerror (errno)); } fflush (bgp_dump_all.fp); }
/* Dump BGP status change. */ void bgp_dump_state (struct peer *peer, int status_old, int status_new) { struct stream *obuf; /* If dump file pointer is disabled return immediately. */ if (bgp_dump_all.fp == NULL) return; /* Make dump stream. */ obuf = bgp_dump_obuf; stream_reset (obuf); bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4, bgp_dump_all.type); bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/ stream_putw (obuf, status_old); stream_putw (obuf, status_new); /* Set length. */ bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); /* Write to the stream. */ fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); fflush (bgp_dump_all.fp); }
static void rip_auth_write_leading_rte ( struct stream *s, struct rip_interface *ri, const u_int8_t key_id, char *auth_str, u_int16_t main_body_len ) { u_int8_t dlen; if (IS_RIP_DEBUG_AUTH) zlog_debug ("writing authentication header for %uB of main body", main_body_len); stream_putw (s, RIP_FAMILY_AUTH); switch (ri->auth_type) { case RIP_AUTH_SIMPLE_PASSWORD: { u_int8_t padded_simple_password[RIP_AUTH_SIMPLE_SIZE] = { 0 }; memcpy (padded_simple_password, auth_str, MIN (RIP_AUTH_SIMPLE_SIZE, strlen (auth_str))); stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD); stream_put (s, padded_simple_password, RIP_AUTH_SIMPLE_SIZE); break; } case RIP_AUTH_HASH: if (IS_RIP_DEBUG_AUTH) zlog_debug ("hash algorithm is '%s'", LOOKUP (hash_algo_str, ri->hash_algo)); stream_putw (s, RIP_AUTH_HASH); stream_putw (s, main_body_len); stream_putc (s, key_id); switch (ri->hash_algo) { case HASH_KEYED_MD5: /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds * however expect RIP_HEADER_SIZE + HASH_SIZE_MD5 so we allow for this * to be configurable. */ dlen = ri->md5_auth_len; break; case HASH_HMAC_SHA1: case HASH_HMAC_SHA256: case HASH_HMAC_SHA384: case HASH_HMAC_SHA512: dlen = hash_digest_length[ri->hash_algo]; break; default: assert (0); } if (IS_RIP_DEBUG_AUTH) zlog_debug ("declared auth data length is %uB", dlen); stream_putc (s, dlen); stream_putl (s, time (NULL)); /* Sequence Number (non-decreasing). */ stream_putl (s, 0); /* reserved, MBZ */ stream_putl (s, 0); /* reserved, MBZ */ break; default: assert (0); } }
void zclient_create_header (struct stream *s, uint16_t command) { /* length placeholder, caller can update */ stream_putw (s, ZEBRA_HEADER_SIZE); stream_putc (s, ZEBRA_HEADER_MARKER); stream_putc (s, ZSERV_VERSION); stream_putw (s, command); }
void tipc_packet_create_header (struct stream *s, uint16_t cmd) { /* length placeholder, caller can update */ stream_putw (s, TIPC_PACKET_HEADER_SIZE); stream_putc (s, TIPC_PACKET_HEADER_MARKER); stream_putc (s, TIPC_PACKET_VERSION); stream_putw (s, cmd); }
/* write RIP-2 hash authentication data trailer */ static void rip_auth_write_trailer (struct stream *s, struct rip_interface *ri, char *auth_str) { unsigned char digest[RIP_AUTH_MAX_SIZE]; unsigned hash_error; if (IS_RIP_DEBUG_AUTH) zlog_debug ("writing authentication trailer after %zuB of main body", stream_get_endp (s)); assert (ri->auth_type == RIP_AUTH_HASH); /* Set authentication data. */ stream_putw (s, RIP_FAMILY_AUTH); stream_putw (s, RIP_AUTH_DATA); /* Generate a digest for the RIP packet and write it to the packet. */ switch (ri->hash_algo) { case HASH_KEYED_MD5: if (IS_RIP_DEBUG_AUTH) zlog_debug ("%s: %zuB of input buffer, %zuB of key", __func__, stream_get_endp (s), strlen (auth_str)); hash_error = hash_make_keyed_md5 ((caddr_t) STREAM_DATA (s), stream_get_endp (s), auth_str, strlen (auth_str), digest); stream_write (s, digest, HASH_SIZE_MD5); break; #ifdef HAVE_LIBGCRYPT case HASH_HMAC_SHA1: case HASH_HMAC_SHA256: case HASH_HMAC_SHA384: case HASH_HMAC_SHA512: { u_int8_t compr_auth_str[RIP_AUTH_MAX_SIZE]; size_t compr_authlen; /* RFC4822 2.5: Fill Apad, process whole packet with HMAC rounds. */ size_t saved_endp = stream_get_endp (s); hash_key_compress_rfc4822 (ri->hash_algo, auth_str, strlen (auth_str), compr_auth_str, &compr_authlen); stream_write (s, hash_apad_sha512, hash_digest_length[ri->hash_algo]); if (IS_RIP_DEBUG_AUTH) zlog_debug ("%s: %zuB of input buffer, %zuB of key (%zuB compressed)", __func__, stream_get_endp (s), strlen (auth_str), compr_authlen); hash_error = hash_make_hmac (ri->hash_algo, STREAM_DATA (s), stream_get_endp (s), compr_auth_str, compr_authlen, STREAM_DATA (s) + saved_endp); break; } #endif /* HAVE_LIBGCRYPT */ default: assert (0); } if (hash_error) if (IS_RIP_DEBUG_AUTH) zlog_debug ("hash function returned error %u", hash_error); }
/* Dump common header. */ void bgp_dump_header (struct stream *obuf, int type, int subtype) { time_t now; /* Set header. */ time (&now); /* Put dump packet header. */ stream_putl (obuf, now); stream_putw (obuf, type); stream_putw (obuf, subtype); stream_putl (obuf, 0); /* len */ }
/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ int zsend_router_id_update (struct zserv *client, struct prefix *p) { struct stream *s; int blen; /* Check this client need interface information. */ if (!client->ridinfo) return -1; s = client->obuf; stream_reset (s); /* Place holder for size. */ stream_putw (s, 0); /* Message type. */ stream_putc (s, ZEBRA_ROUTER_ID_UPDATE); /* Prefix information. */ stream_putc (s, p->family); blen = prefix_blen (p); stream_put (s, &p->u.prefix, blen); stream_putc (s, p->prefixlen); /* Write packet size. */ stream_putw_at (s, 0, stream_get_endp (s)); return writen (client->sock, s->data, stream_get_endp (s)); }
/* * The cmd passed to zsend_interface_update may be ZEBRA_INTERFACE_UP or * ZEBRA_INTERFACE_DOWN. * * The ZEBRA_INTERFACE_UP message is sent from the zebra server to * the clients in one of 2 situations: * - an if_up is detected e.g., as a result of an RTM_IFINFO message * - a vty command modifying the bandwidth of an interface is received. * The ZEBRA_INTERFACE_DOWN message is sent when an if_down is detected. */ int zsend_interface_update (int cmd, struct zserv *client, struct interface *ifp) { struct stream *s; /* Check this client need interface information. */ if (! client->ifinfo) return -1; s = client->obuf; stream_reset (s); /* Place holder for size. */ stream_putw (s, 0); /* Zebra command. */ stream_putc (s, cmd); /* Interface information. */ stream_put (s, ifp->name, INTERFACE_NAMSIZ); stream_putl (s, ifp->ifindex); stream_putc (s, ifp->status); stream_putl (s, ifp->flags); stream_putl (s, ifp->metric); stream_putl (s, ifp->mtu); stream_putl (s, ifp->mtu6); stream_putl (s, ifp->bandwidth); /* Write packet size. */ stream_putw_at (s, 0, stream_get_endp (s)); zebra_server_send_message (client->sock, s->data, stream_get_endp (s)); return 0; }
int main (void) { struct stream *s; s = stream_new (1024); stream_putc (s, ham); stream_putw (s, ham); stream_putl (s, ham); stream_putq (s, ham); print_stream (s); stream_resize (s, stream_get_endp (s)); print_stream (s); printf ("c: 0x%hhx\n", stream_getc (s)); printf ("w: 0x%hx\n", stream_getw (s)); printf ("l: 0x%x\n", stream_getl (s)); printf ("q: 0x%" PRIu64 "\n", stream_getq (s)); return 0; }
/* * "xdr_encode"-like interface that allows daemon (client) to send * a message to zebra server for a route that needs to be * added/deleted to the kernel. Info about the route is specified * by the caller in a struct zapi_ipv4. zapi_ipv4_read() then writes * the info down the zclient socket using the stream_* functions. * * The corresponding read ("xdr_decode") function on the server * side is zread_ipv4_add()/zread_ipv4_delete(). * * 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Length (2) | Command | Route Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | ZEBRA Flags | Message Flags | Prefix length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination IPv4 Prefix for route | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Nexthop count | * +-+-+-+-+-+-+-+-+ * * * A number of IPv4 nexthop(s) or nexthop interface index(es) are then * described, as per the Nexthop count. Each nexthop described as: * * +-+-+-+-+-+-+-+-+ * | Nexthop Type | Set to one of ZEBRA_NEXTHOP_* * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | IPv4 Nexthop address or Interface Index number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Alternatively, if the flags field has ZEBRA_FLAG_BLACKHOLE or * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_ * nexthop information is provided, and the message describes a prefix * to blackhole or reject route. * * If ZAPI_MESSAGE_DISTANCE is set, the distance value is written as a 1 * byte value. * * If ZAPI_MESSAGE_METRIC is set, the metric value is written as an 8 * byte value. * * XXX: No attention paid to alignment. */ int zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, struct zapi_ipv4 *api) { int i; int psize; struct stream *s; /* Reset stream. */ s = zclient->obuf; stream_reset (s); zclient_create_header (s, cmd); /* Put type and nexthop. */ stream_putc (s, api->type); stream_putc (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); /* Put prefix information. */ psize = PSIZE (p->prefixlen); stream_putc (s, p->prefixlen); stream_write (s, (u_char *) & p->prefix, psize); /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) { if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE)) { stream_putc (s, 1); stream_putc (s, ZEBRA_NEXTHOP_BLACKHOLE); /* XXX assert(api->nexthop_num == 0); */ /* XXX assert(api->ifindex_num == 0); */ } else stream_putc (s, api->nexthop_num + api->ifindex_num); for (i = 0; i < api->nexthop_num; i++) { stream_putc (s, ZEBRA_NEXTHOP_IPV4); stream_put_in_addr (s, api->nexthop[i]); } for (i = 0; i < api->ifindex_num; i++) { stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); stream_putl (s, api->ifindex[i]); } } if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) stream_putc (s, api->distance); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); /* Put length at the first point of the stream. */ stream_putw_at (s, 0, stream_get_endp (s)); return zclient_send_message(zclient); }
int zsend_interface_delete (struct zserv *client, struct interface *ifp) { struct stream *s; /* Check this client need interface information. */ if (! client->ifinfo) return -1; s = client->obuf; stream_reset (s); /* Packet length placeholder. */ stream_putw (s, 0); /* Interface information. */ stream_putc (s, ZEBRA_INTERFACE_DELETE); stream_put (s, ifp->name, INTERFACE_NAMSIZ); stream_putl (s, ifp->ifindex); stream_putc (s, ifp->status); stream_putl (s, ifp->flags); stream_putl (s, ifp->metric); stream_putl (s, ifp->mtu); stream_putl (s, ifp->mtu6); stream_putl (s, ifp->bandwidth); /* Write packet length. */ stream_putw_at (s, 0, stream_get_endp (s)); zebra_server_send_message (client->sock, s->data, stream_get_endp (s)); return 0; }
static int zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) { struct stream *s; struct rib *rib; unsigned long nump; u_char num; struct nexthop *nexthop; /* Lookup nexthop. */ rib = rib_lookup_ipv4 (p); /* Get output stream. */ s = client->obuf; stream_reset (s); /* Fill in result. */ stream_putw (s, 0); stream_putc (s, ZEBRA_IPV4_IMPORT_LOOKUP); stream_put_in_addr (s, &p->prefix); if (rib) { stream_putl (s, rib->metric); num = 0; nump = s->putp; stream_putc (s, 0); for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { stream_putc (s, nexthop->type); switch (nexthop->type) { case ZEBRA_NEXTHOP_IPV4: stream_put_in_addr (s, &nexthop->gate.ipv4); break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: stream_putl (s, nexthop->ifindex); break; default: /* do nothing */ break; } num++; } stream_putc_at (s, nump, num); } else { stream_putl (s, 0); stream_putc (s, 0); } stream_putw_at (s, 0, stream_get_endp (s)); zebra_server_send_message (client->sock, s->data, stream_get_endp (s)); return 0; }
int zapi_ipv6_route (u_char cmd, struct_zclient *zclient, struct prefix_ipv6 *p, struct zapi_ipv6 *api) { int i; int psize; struct stream *s; /* Reset stream. */ s = zclient->obuf; stream_reset (s); /* Length place holder. */ stream_putw (s, 0); /* Put command, type and nexthop. */ stream_putc (s, cmd); stream_putc (s, api->type); stream_putc (s, api->flags); stream_putc (s, api->message); /* Put prefix information. */ psize = PSIZE (p->prefixlen); stream_putc (s, p->prefixlen); stream_write (s, (u_char *)&p->prefix, psize); /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) { stream_putc (s, api->nexthop_num + api->ifindex_num); for (i = 0; i < api->nexthop_num; i++) { stream_putc (s, ZEBRA_NEXTHOP_IPV6); stream_write (s, (u_char *)api->nexthop[i], 16); } for (i = 0; i < api->ifindex_num; i++) { stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); stream_putl (s, api->ifindex[i]); } } if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) stream_putc (s, api->distance); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); /* Put length at the first point of the stream. */ stream_putw_at (s, 0, stream_get_endp (s)); return writen (zclient->sock, s->data, stream_get_endp (s)); }
/* Interface address is added/deleted. Send ZEBRA_INTERFACE_ADDRESS_ADD or * ZEBRA_INTERFACE_ADDRESS_DELETE to the client. * * A ZEBRA_INTERFACE_ADDRESS_ADD is sent in the following situations: * - in response to a 3-byte ZEBRA_INTERFACE_ADD request * from the client, after the ZEBRA_INTERFACE_ADD has been * sent from zebra to the client * - redistribute new address info to all clients in the following situations * - at startup, when zebra figures out the available interfaces * - when an interface is added (where support for * RTM_IFANNOUNCE or AF_NETLINK sockets is available), or when * an interface is marked IFF_UP (i.e., an RTM_IFINFO message is * received) * - for the vty commands "ip address A.B.C.D/M [<secondary>|<label LINE>]" * and "no bandwidth <1-10000000>", "ipv6 address X:X::X:X/M" * - when an RTM_NEWADDR message is received from the kernel, * * The call tree that triggers ZEBRA_INTERFACE_ADDRESS_DELETE: * * zsend_interface_address(DELETE) * ^ * | * zebra_interface_address_delete_update * ^ ^ ^ * | | if_delete_update (not called on * | | Solaris) * ip_address_uninstall connected_delete_ipv4 * [ipv6_addresss_uninstall] [connected_delete_ipv6] * ^ ^ * | | * | RTM_NEWADDR on routing/netlink socket * | * vty commands: * "no ip address A.B.C.D/M [label LINE]" * "no ip address A.B.C.D/M secondary" * ["no ipv6 address X:X::X:X/M"] * */ int zsend_interface_address (int cmd, struct zserv *client, struct interface *ifp, struct connected *ifc) { int blen; struct stream *s; struct prefix *p; /* Check this client need interface information. */ if (! client->ifinfo) return -1; s = client->obuf; stream_reset (s); /* Place holder for size. */ stream_putw (s, 0); stream_putc (s, cmd); stream_putl (s, ifp->ifindex); /* Interface address flag. */ stream_putc (s, ifc->flags); /* Prefix information. */ p = ifc->address; stream_putc (s, p->family); blen = prefix_blen (p); stream_put (s, &p->u.prefix, blen); /* * XXX gnu version does not send prefixlen for ZEBRA_INTERFACE_ADDRESS_DELETE * but zebra_interface_address_delete_read() in the gnu version * expects to find it */ stream_putc (s, p->prefixlen); /* Destination. */ p = ifc->destination; if (p) stream_put (s, &p->u.prefix, blen); else stream_put (s, NULL, blen); /* Write packet size. */ stream_putw_at (s, 0, stream_get_endp (s)); zebra_server_send_message (client->sock, s->data, stream_get_endp (s)); return 0; }
/* Send simple Zebra message. */ int #undef zclient zebra_message_send (struct_zclient *zclient, int command) { struct stream *s; /* Get zclient output buffer. */ s = zclient->obuf; stream_reset (s); /* Send very simple command only Zebra message. */ stream_putw (s, 3); stream_putc (s, command); return writen (zclient->sock, s->data, 3); }
/* * This function is called in the following situations: * - in response to a 3-byte ZEBRA_INTERFACE_ADD request * from the client. * - at startup, when zebra figures out the available interfaces * - when an interface is added (where support for * RTM_IFANNOUNCE or AF_NETLINK sockets is available), or when * an interface is marked IFF_UP (i.e., an RTM_IFINFO message is * received) */ int zsend_interface_add (struct zserv *client, struct interface *ifp) { struct stream *s; /* Check this client need interface information. */ if (! client->ifinfo) return -1; s = client->obuf; stream_reset (s); /* Place holder for size. */ stream_putw (s, 0); /* Message type. */ stream_putc (s, ZEBRA_INTERFACE_ADD); /* Interface information. */ stream_put (s, ifp->name, INTERFACE_NAMSIZ); stream_putl (s, ifp->ifindex); stream_putc (s, ifp->status); stream_putl (s, ifp->flags); stream_putl (s, ifp->metric); stream_putl (s, ifp->mtu); stream_putl (s, ifp->mtu6); stream_putl (s, ifp->bandwidth); #ifdef HAVE_SOCKADDR_DL stream_put (s, &ifp->sdl, sizeof (ifp->sdl)); #else stream_putl (s, ifp->hw_addr_len); if (ifp->hw_addr_len) stream_put (s, ifp->hw_addr, ifp->hw_addr_len); #endif /* HAVE_SOCKADDR_DL */ /* Write packet size. */ stream_putw_at (s, 0, stream_get_endp (s)); zebra_server_send_message (client->sock, s->data, stream_get_endp (s)); return 0; }
/* * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will * then set/unset redist[type] in the client handle (a struct zserv) for the * sending client */ int zebra_redistribute_send (int command, int sock, int type) { int ret; struct stream *s; s = stream_new (ZEBRA_MAX_PACKET_SIZ); /* Total length of the messages. */ stream_putw (s, 4); stream_putc (s, command); stream_putc (s, type); ret = writen (sock, s->data, 4); stream_free (s); return ret; }
/* Dump common information. */ static void bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) { char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* Source AS number and Destination AS number. */ if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) ) { stream_putl (obuf, peer->as); stream_putl (obuf, peer->local_as); } else { stream_putw (obuf, peer->as); stream_putw (obuf, peer->local_as); } if (peer->su.sa.sa_family == AF_INET) { stream_putw (obuf, peer->ifindex); stream_putw (obuf, AFI_IP); stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN); if (peer->su_local) stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN); else stream_put (obuf, empty, IPV4_MAX_BYTELEN); } #ifdef HAVE_IPV6 else if (peer->su.sa.sa_family == AF_INET6) { /* Interface Index and Address family. */ stream_putw (obuf, peer->ifindex); stream_putw (obuf, AFI_IP6); /* Source IP Address and Destination IP Address. */ stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); if (peer->su_local) stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN); else stream_put (obuf, empty, IPV6_MAX_BYTELEN); } #endif /* HAVE_IPV6 */ }
/* Dump common information. */ void bgp_dump_common (struct stream *obuf, struct peer *peer) { char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* Source AS number and Destination AS number. */ stream_putw (obuf, peer->as); stream_putw (obuf, peer->local_as); if (peer->afc[AFI_IP][SAFI_UNICAST]) { stream_putw (obuf, peer->ifindex); stream_putw (obuf, AFI_IP); stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN); if (peer->su_local) stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN); else stream_put (obuf, empty, IPV4_MAX_BYTELEN); } #ifdef HAVE_IPV6 else if (peer->afc[AFI_IP6][SAFI_UNICAST]) { /* Interface Index and Address family. */ stream_putw (obuf, peer->ifindex); stream_putw (obuf, AFI_IP6); /* Source IP Address and Destination IP Address. */ stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); if (peer->su_local) stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN); else stream_put (obuf, empty, IPV6_MAX_BYTELEN); } #endif /* HAVE_IPV6 */ }
/* Runs under child process. */ static unsigned int bgp_dump_routes_func (int afi, int first_run, unsigned int seq) { struct stream *obuf; struct bgp_info *info; struct bgp_node *rn; struct bgp *bgp; struct bgp_table *table; bgp = bgp_get_default (); if (!bgp) return seq; if (bgp_dump_routes.fp == NULL) return seq; /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers, so this should only be done on the first call to bgp_dump_routes_func. ( this function will be called once for ipv4 and once for ipv6 ) */ if(first_run) bgp_dump_routes_index_table(bgp); obuf = bgp_dump_obuf; stream_reset(obuf); /* Walk down each BGP route. */ table = bgp->rib[afi][SAFI_UNICAST]; for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) { if(!rn->info) continue; stream_reset(obuf); /* MRT header */ if (afi == AFI_IP) bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST, BGP_DUMP_ROUTES); else if (afi == AFI_IP6) bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST, BGP_DUMP_ROUTES); /* Sequence number */ stream_putl(obuf, seq); /* Prefix length */ stream_putc (obuf, rn->p.prefixlen); /* Prefix */ if (afi == AFI_IP) { /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8); } else if (afi == AFI_IP6) { /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8); } /* Save where we are now, so we can overwride the entry count later */ int sizep = stream_get_endp(obuf); /* Entry count */ uint16_t entry_count = 0; /* Entry count, note that this is overwritten later */ stream_putw(obuf, 0); for (info = rn->info; info; info = info->next) { entry_count++; /* Peer index */ stream_putw(obuf, info->peer->table_dump_index); /* Originated */ #ifdef HAVE_CLOCK_MONOTONIC stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime)); #else stream_putl (obuf, info->uptime); #endif /* HAVE_CLOCK_MONOTONIC */ /* Dump attribute. */ /* Skip prefix & AFI/SAFI for MP_NLRI */ bgp_dump_routes_attr (obuf, info->attr, &rn->p); } /* Overwrite the entry count, now that we know the right number */ stream_putw_at (obuf, sizep, entry_count); seq++; bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); } fflush (bgp_dump_routes.fp); return seq; }
static void bgp_dump_routes_index_table(struct bgp *bgp) { struct peer *peer; struct listnode *node; uint16_t peerno = 0; struct stream *obuf; obuf = bgp_dump_obuf; stream_reset (obuf); /* MRT header */ bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE, BGP_DUMP_ROUTES); /* Collector BGP ID */ stream_put_in_addr (obuf, &bgp->router_id); /* View name */ if(bgp->name) { stream_putw (obuf, strlen(bgp->name)); stream_put(obuf, bgp->name, strlen(bgp->name)); } else { stream_putw(obuf, 0); } /* Peer count */ stream_putw (obuf, listcount(bgp->peer)); /* Walk down all peers */ for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) { /* Peer's type */ if (sockunion_family(&peer->su) == AF_INET) { stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP); } else if (sockunion_family(&peer->su) == AF_INET6) { stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6); } /* Peer's BGP ID */ stream_put_in_addr (obuf, &peer->remote_id); /* Peer's IP address */ if (sockunion_family(&peer->su) == AF_INET) { stream_put_in_addr (obuf, &peer->su.sin.sin_addr); } else if (sockunion_family(&peer->su) == AF_INET6) { stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); } /* Peer's AS number. */ /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */ stream_putl (obuf, peer->as); /* Store the peer number for this peer */ peer->table_dump_index = peerno; peerno++; } bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); fflush (bgp_dump_routes.fp); }
void isis_zebra_route_add_ipv4 (struct prefix *prefix, struct isis_route_info *route_info) { u_char message, flags; int psize; struct stream *stream; struct isis_nexthop *nexthop; struct listnode *node; if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC)) return; if (zclient->redist[ZEBRA_ROUTE_ISIS]) { message = 0; flags = 0; SET_FLAG (message, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (message, ZAPI_MESSAGE_METRIC); #if 0 SET_FLAG (message, ZAPI_MESSAGE_DISTANCE); #endif stream = zclient->obuf; stream_reset (stream); /* Length place holder. */ stream_putw (stream, 0); /* command */ stream_putc (stream, ZEBRA_IPV4_ROUTE_ADD); /* type */ stream_putc (stream, ZEBRA_ROUTE_ISIS); /* flags */ stream_putc (stream, flags); /* message */ stream_putc (stream, message); /* prefix information */ psize = PSIZE (prefix->prefixlen); stream_putc (stream, prefix->prefixlen); stream_write (stream, (u_char *) & prefix->u.prefix4, psize); stream_putc (stream, listcount (route_info->nexthops)); /* Nexthop, ifindex, distance and metric information */ for (node = listhead (route_info->nexthops); node; nextnode (node)) { nexthop = getdata (node); /* FIXME: can it be ? */ if (nexthop->ip.s_addr != INADDR_ANY) { stream_putc (stream, ZEBRA_NEXTHOP_IPV4); stream_put_in_addr (stream, &nexthop->ip); } else { stream_putc (stream, ZEBRA_NEXTHOP_IFINDEX); stream_putl (stream, nexthop->ifindex); } } #if 0 if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE)) stream_putc (stream, route_info->depth); #endif if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) stream_putl (stream, route_info->cost); stream_putw_at (stream, 0, stream_get_endp (stream)); writen (zclient->sock, stream->data, stream_get_endp (stream)); } }
void ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; u_char flags; int psize; struct stream *s; struct ospf_path *path; struct listnode *node; if (zclient->redist[ZEBRA_ROUTE_OSPF]) { message = 0; flags = 0; /* OSPF pass nexthop and metric */ SET_FLAG (message, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (message, ZAPI_MESSAGE_METRIC); /* Distance value. */ distance = ospf_distance_apply (p, or); if (distance) SET_FLAG (message, ZAPI_MESSAGE_DISTANCE); /* Make packet. */ s = zclient->obuf; stream_reset (s); /* Put command, type, flags, message. */ zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putc (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); /* Put prefix information. */ psize = PSIZE (p->prefixlen); stream_putc (s, p->prefixlen); stream_write (s, (u_char *) & p->prefix, psize); /* Nexthop count. */ stream_putc (s, or->paths->count); /* Nexthop, ifindex, distance and metric information. */ for (ALL_LIST_ELEMENTS_RO (or->paths, node, path)) { if (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0) { stream_putc (s, ZEBRA_NEXTHOP_IPV4_IFINDEX); stream_put_in_addr (s, &path->nexthop); stream_putl (s, path->ifindex); } else if (path->nexthop.s_addr != INADDR_ANY) { stream_putc (s, ZEBRA_NEXTHOP_IPV4); stream_put_in_addr (s, &path->nexthop); } else { stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); if (path->ifindex) stream_putl (s, path->ifindex); else stream_putl (s, 0); } if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) { char buf[2][INET_ADDRSTRLEN]; zlog_debug("Zebra: Route add %s/%d nexthop %s", inet_ntop(AF_INET, &p->prefix, buf[0], sizeof(buf[0])), p->prefixlen, inet_ntop(AF_INET, &path->nexthop, buf[1], sizeof(buf[1]))); } } if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE)) stream_putc (s, distance); if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) { if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL) stream_putl (s, or->cost + or->u.ext.type2_cost); else if (or->path_type == OSPF_PATH_TYPE2_EXTERNAL) stream_putl (s, or->u.ext.type2_cost); else stream_putl (s, or->cost); } stream_putw_at (s, 0, stream_get_endp (s)); zclient_send_message(zclient); }
/* Take a sequence of payload (routing) RTE structures, decide on particular * authentication required for the given interface and build a complete RIP * packet in a stream structure. The packet will consist of header, optional * heading RTE, the payload RTEs and optional trailing data. Return the stream. */ int rip_auth_make_packet ( struct rip_interface * ri, struct stream * packet, struct stream * rtes, const u_int8_t version, const u_int8_t command ) { struct key *key = NULL; char *auth_str = NULL; if (IS_RIP_DEBUG_AUTH) zlog_debug ("interface auth type is '%s', inet RTEs payload size is %zuB", LOOKUP (rip_ffff_type_str, ri->auth_type), stream_get_endp (rtes)); /* packet header, unconditional */ stream_reset (packet); stream_putc (packet, command); stream_putc (packet, version); stream_putw (packet, 0); /* authentication leading RTE, conditional */ if (version == RIPv2 && ri->auth_type != RIP_NO_AUTH) { if (ri->key_chain) { struct keychain *keychain; keychain = keychain_lookup (ri->key_chain); if (keychain) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("trying configured key chain '%s'", ri->key_chain); key = key_lookup_for_send (keychain); } else { if (IS_RIP_DEBUG_AUTH) zlog_debug ("key chain '%s' is configured, but does not exist", ri->key_chain); } } /* Pick correct auth string for sends, prepare auth_str buffer for use. * (left justified and padded). * * presumes one of ri or key is valid, and that the auth strings they point * to are nul terminated. If neither are present, auth_str will be fully * zero padded. * */ if (key && key->string) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("using keychain '%s', key %u for sending", ri->key_chain, key->index); auth_str = key->string; } else if (ri->auth_str) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("using interface authentication string"); auth_str = ri->auth_str; } if (auth_str == NULL) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("authentication string lookup failed"); return -1; } rip_auth_write_leading_rte (packet, ri, key ? key->index % 256 : 1, auth_str, RIP_HEADER_SIZE + RIP_RTE_SIZE + stream_get_endp (rtes)); } /* RTEs payload, unconditional */ if (stream_get_endp (rtes) % RIP_RTE_SIZE) { zlog_err ("%s: malformed input RTE buffer", __func__); return -1; } stream_write (packet, STREAM_DATA (rtes), stream_get_endp (rtes)); stream_reset (rtes); /* authentication trailing data, even more conditional */ if (version == RIPv2 && ri->auth_type == RIP_AUTH_HASH) rip_auth_write_trailer (packet, ri, auth_str); return 0; }
static struct bgp_info * bgp_dump_route_node_record (int afi, struct bgp_node *rn, struct bgp_info *info, unsigned int seq) { struct stream *obuf; size_t sizep; size_t endp; obuf = bgp_dump_obuf; stream_reset (obuf); /* MRT header */ if (afi == AFI_IP) bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST, BGP_DUMP_ROUTES); else if (afi == AFI_IP6) bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST, BGP_DUMP_ROUTES); /* Sequence number */ stream_putl (obuf, seq); /* Prefix length */ stream_putc (obuf, rn->p.prefixlen); /* Prefix */ if (afi == AFI_IP) { /* We'll dump only the useful bits (those not 0), but have to * align on 8 bits */ stream_write (obuf, (u_char *) &rn->p.u.prefix4, (rn->p.prefixlen + 7) / 8); } else if (afi == AFI_IP6) { /* We'll dump only the useful bits (those not 0), but have to * align on 8 bits */ stream_write (obuf, (u_char *) &rn->p.u.prefix6, (rn->p.prefixlen + 7) / 8); } /* Save where we are now, so we can overwride the entry count later */ sizep = stream_get_endp (obuf); /* Entry count */ uint16_t entry_count = 0; /* Entry count, note that this is overwritten later */ stream_putw (obuf, 0); endp = stream_get_endp (obuf); for (; info; info = info->next) { size_t cur_endp; /* Peer index */ stream_putw (obuf, info->peer->table_dump_index); /* Originated */ #ifdef HAVE_CLOCK_MONOTONIC stream_putl (obuf, time (NULL) - (bgp_clock () - info->uptime)); #else stream_putl (obuf, info->uptime); #endif /* HAVE_CLOCK_MONOTONIC */ /* Dump attribute. */ /* Skip prefix & AFI/SAFI for MP_NLRI */ bgp_dump_routes_attr (obuf, info->attr, &rn->p); cur_endp = stream_get_endp (obuf); if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE) { stream_set_endp (obuf, endp); break; } entry_count++; endp = cur_endp; } /* Overwrite the entry count, now that we know the right number */ stream_putw_at (obuf, sizep, entry_count); bgp_dump_set_size (obuf, MSG_TABLE_DUMP_V2); fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); return info; }
void bgp_dump_routes_entry (struct prefix *p, struct bgp_info *info, int afi, int type, unsigned int seq) { struct stream *obuf; struct attr *attr; struct peer *peer; int plen; int safi = 0; /* Make dump stream. */ obuf = bgp_dump_obuf; stream_reset (obuf); attr = info->attr; peer = info->peer; /* We support MRT's old format. */ if (type == MSG_TABLE_DUMP) { bgp_dump_header (obuf, MSG_TABLE_DUMP, afi); stream_putw (obuf, 0); /* View # */ stream_putw (obuf, seq); /* Sequence number. */ } else { bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY); stream_putl (obuf, info->uptime); /* Time Last Change */ stream_putw (obuf, afi); /* Address Family */ stream_putc (obuf, safi); /* SAFI */ } if (afi == AFI_IP) { if (type == MSG_TABLE_DUMP) { /* Prefix */ stream_put_in_addr (obuf, &p->u.prefix4); stream_putc (obuf, p->prefixlen); /* Status */ stream_putc (obuf, 1); /* Originated */ stream_putl (obuf, info->uptime); /* Peer's IP address */ stream_put_in_addr (obuf, &peer->su.sin.sin_addr); /* Peer's AS number. */ stream_putw (obuf, peer->as); /* Dump attribute. */ bgp_dump_routes_attr (obuf, attr, p); } else { /* Next-Hop-Len */ stream_putc (obuf, IPV4_MAX_BYTELEN); stream_put_in_addr (obuf, &attr->nexthop); stream_putc (obuf, p->prefixlen); plen = PSIZE (p->prefixlen); stream_put (obuf, &p->u.prefix4, plen); bgp_dump_routes_attr (obuf, attr, p); } } #ifdef HAVE_IPV6 else if (afi == AFI_IP6) { if (type == MSG_TABLE_DUMP) { /* Prefix */ stream_write (obuf, (u_char *)&p->u.prefix6, IPV6_MAX_BYTELEN); stream_putc (obuf, p->prefixlen); /* Status */ stream_putc (obuf, 1); /* Originated */ stream_putl (obuf, info->uptime); /* Peer's IP address */ stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); /* Peer's AS number. */ stream_putw (obuf, peer->as); /* Dump attribute. */ bgp_dump_routes_attr (obuf, attr, p); } else { ; } } #endif /* HAVE_IPV6 */ /* Set length. */ bgp_dump_set_size (obuf, type); fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump_routes.fp); fflush (bgp_dump_routes.fp); }
/* * The zebra server sends the clients a ZEBRA_IPV4_ROUTE_ADD or a * ZEBRA_IPV6_ROUTE_ADD via zsend_route_multipath in the following * situations: * - when the client starts up, and requests default information * by sending a ZEBRA_REDISTRIBUTE_DEFAULT_ADD to the zebra server, in the * - case of rip, ripngd, ospfd and ospf6d, when the client sends a * ZEBRA_REDISTRIBUTE_ADD as a result of the "redistribute" vty cmd, * - when the zebra server redistributes routes after it updates its rib * * The zebra server sends clients a ZEBRA_IPV4_ROUTE_DELETE or a * ZEBRA_IPV6_ROUTE_DELETE via zsend_route_multipath when: * - a "ip route" or "ipv6 route" vty command is issued, a prefix is * - deleted from zebra's rib, and this info * has to be redistributed to the clients * * XXX The ZEBRA_IPV*_ROUTE_ADD message is also sent by the client to the * zebra server when the client wants to tell the zebra server to add a * route to the kernel (zapi_ipv4_add etc. ). Since it's essentially the * same message being sent back and forth, this function and * zapi_ipv{4,6}_{add, delete} should be re-written to avoid code * duplication. */ int zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p, struct rib *rib) { int psize; struct stream *s; struct nexthop *nexthop; unsigned long nhnummark = 0; int nhnum = 0; u_char zapi_flags = ZAPI_MESSAGE_NEXTHOP | ZAPI_MESSAGE_IFINDEX; s = client->obuf; stream_reset (s); /* Place holder for size. */ stream_putw (s, 0); /* Put command, type and nexthop. */ stream_putc (s, cmd); stream_putc (s, rib->type); stream_putc (s, rib->flags); /* * XXX no need to set ZAPI_MESSAGE_NEXTHOP if we are going to * send empty nexthop? */ if (cmd == ZEBRA_IPV4_ROUTE_ADD || ZEBRA_IPV6_ROUTE_ADD) zapi_flags |= ZAPI_MESSAGE_METRIC; stream_putc (s, zapi_flags); /* Prefix. */ psize = PSIZE (p->prefixlen); stream_putc (s, p->prefixlen); stream_write (s, (u_char *) & p->u.prefix, psize); /* * XXX The message format sent by zebra below does not match the format * of the corresponding message expected by the zebra server * itself (e.g., see zread_ipv4_add). The nexthop_num is not set correctly, * (is there a bug on the client side if more than one segment is sent?) * nexthop ZEBRA_NEXTHOP_IPV4 is never set, ZEBRA_NEXTHOP_IFINDEX * is hard-coded. */ /* Nexthop */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { nhnummark = stream_get_putp (s); stream_putc (s, 1); /* placeholder */ nhnum++; switch(nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: stream_put_in_addr (s, &nexthop->gate.ipv4); break; #ifdef HAVE_IPV6 case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFNAME: stream_write (s, (u_char *) &nexthop->gate.ipv6, 16); break; #endif default: if (cmd == ZEBRA_IPV4_ROUTE_ADD || cmd == ZEBRA_IPV4_ROUTE_DELETE) { struct in_addr empty; memset (&empty, 0, sizeof (struct in_addr)); stream_write (s, (u_char *) &empty, IPV4_MAX_BYTELEN); } else { struct in6_addr empty; memset (&empty, 0, sizeof (struct in6_addr)); stream_write (s, (u_char *) &empty, IPV6_MAX_BYTELEN); } } /* Interface index. */ stream_putc (s, 1); stream_putl (s, nexthop->ifindex); break; } } /* Metric */ stream_putl (s, rib->metric); /* Write next-hop number */ if (nhnummark) stream_putc_at (s, nhnummark, nhnum); /* Write packet size. */ stream_putw_at (s, 0, stream_get_endp (s)); zebra_server_send_message (client->sock, s->data, stream_get_endp (s)); return 0; }