summaryrefslogtreecommitdiffstats
path: root/src
AgeCommit message (Collapse)AuthorFilesLines
2017-02-28Fix warning in generated codeDave Barach1-4/+14
Change-Id: Ie56fca84a8a0ed77ee480e8078e6e9b3f4cef105 Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-28Trace plugin binary API message range allocationDave Barach2-8/+89
Change-Id: I544a5d2906548607b69f999567b92f802fddddbb Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-27BFD: disable debug printsKlement Sekera1-1/+1
Change-Id: I356581f4bdf47b9610b9e50f4f8db9a1510872a7 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2017-02-27vlib: add VLIB_BUFFER_EXT_HDR_VALID flagDamjan Marion4-5/+4
Change-Id: If56c66dd12eded1cc997087de5fd1b975766c4e2 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-02-27Add GPE CLI/API for setting encap modeFilip Tehlar8-4/+319
Change-Id: Id89e23fb5d275572b2356c073dfa0f55719e1a76 Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
2017-02-27fix:vxlan mcast adj - added as ucast dpo adjEyal Bari1-1/+1
Change-Id: Ic2447313075cd46f265202dffaaac894f48ddf6d Signed-off-by: Eyal Bari <ebari@cisco.com>
2017-02-26Load plugins in alphabetical orderDave Barach1-9/+55
API traces contain absolute message numbers. Loading plugins in directory (vs. alphabetical) order makes trace replay fragile. Change-Id: I46b3a3b6a9843a383d42269fca0cf5a789486eaf Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-26BFD: echo functionKlement Sekera8-318/+864
Change-Id: Ib1e301d62b687d4e42434239e7cd412065c28da0 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2017-02-25Add NSH to GPE decap pathFlorin Coras5-20/+56
Change-Id: I97681322fa9ca81736100b4d32eab84868886c7b Signed-off-by: Florin Coras <fcoras@cisco.com>
2017-02-25MFIB: changes to improve route add/delete performanceNeale Ranns3-7/+12
Change-Id: I063d85200d12b09545ae1c373c7fc69112ae3b34 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-24FIB: 1) fix pool realloc during prefix export. 2) don't walk off the end of ↵Neale Ranns3-7/+31
the path-extension vector Change-Id: I8bd8f6917ace089edb1f65bd017b478ee198c03f Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-24VPP-650: handle buffer failure in vlib_buffer_copy(...)Dave Barach4-10/+63
Change-Id: I6aac48d780fcd935818221044eae50067f225175 Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-24Fixed QAT device binding and device unbinding when vpp package is removedRadu Nicolau1-1/+1
Change-Id: I35ad6a42093cad0945df1df09a39c63c4560dce6 Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
2017-02-24MFIB memory leak. free the per-source interface hashNeale Ranns3-6/+3
Change-Id: I0ccb337eb0ed50ccc64193533cd816f6e36e6db5 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-24VPP-279: Document changes for vnet/vnet/devicesBilly McFall2-100/+617
Add doxygen documentation for dpdk CLI commands. Outside of adding documentation to the CLI Commands, modified the CLI code as follows: * The "set dpdk interface placement" command allows the user to move interface/queues to a different thread. But there is only a subset of threads that are valid. Updated the "show dpdk interface placement" command to display all valid threads, even if all interface/queues have been moved off. Updated the "show dpdk interface hqos placement" the same way. * There is a command to modify the Subport attributes, but no way to display the changes. Added a "Subport" section to the "show dpdk interface hqos" command. * Reworked the "set dpdk interface hqos subport" command. - The current implementation had a local rte_sched_subport_params structure and initialized it to default values, then overwrote with what was input. The side effect of this is that if all the current data is non-default, and a new command is entered with just one attribute, all the remaining attrbutes are getting set back to default under the cover. Very confusing for the user. Updated the code to read the current value and overwrite what has changed. - DPDK does not have a read subport data, so no way query the current applied values. The set command was not updating the local copy that is created at init. Modified the code to store the updated values if the DPDK apply function was successful. - Several functions repeated the same code to get a pointer to the local HQoS data. Added a utility function.get_hqos(..), to perform this action. Did not port other code to use new function. * The "set dpdk interface hqos pktfield" allows the user to set the packet fields required for classifiying the incoming packet. The classification is across three fields (subport, pipe, tc). The command was using 0,1,2 to represent these three fields, but had no explanation regarding these magic numbers. Updated the command to take the three tokens (subport, pipe, tc) for more clarity. For legacy sake, still allow 0,1,2 to be entered. Also updated the "show dpdk interface hqos" command to show these tokens. * The "set dpdk interface hqos tctbl" maps an interface and value 0-63 to a traffic class and queue. The "show dpdk interface hqos" command showed the internal DPDK magic number for traffic class and queue. Updated the show command to display what was input instead of the magic number. * The "show dpdk hqos queue" command always returns zeros by default because RTE_SCHED_COLLECT_STATS is not defined in DPDK. Took me a while to figure out why I wasn't getting values returned. So returned an error message if RTE_SCHED_COLLECT_STATS is not defined instead of zeros. Change-Id: I22b640d668245839ee977ef3602175c61d91d24c Signed-off-by: Billy McFall <bmcfall@redhat.com>
2017-02-23Fix vpp built-in version of api_unformat_sw_if_index(...)Dave Barach3-19/+36
Change-Id: I103fe19a1ecbaf3746ec6b957fa1010458cc9fae Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-22Clean up "binary-api" help string, arg parse bugsDave Barach2-2/+8
Change-Id: I12311be8ebd376b8aeac25364d010d70a85c7874 Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-22Fix LISP and ONE crc marcosFilip Tehlar3-11/+26
Change-Id: Icd0dba04d8929456228136d1f25c459bffcc6a7a Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
2017-02-22Support multiple plugin build in the sample-pluginAnlu Yan3-18/+52
This follows the setup in the src/plugins directory, and allows multiple plugin build independent of the main vpp source tree. Change-Id: I9e20f4087d72ad89c6dc3f505bace4628385a40e Signed-off-by: Anlu Yan <ayan@cisco.com>
2017-02-22VPP-635: CLI Memory leak with invalid parameterBilly McFall36-572/+1472
In the CLI parsing, below is a common pattern: /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "x")) x = 1; : else return clib_error_return (0, "unknown input `%U'", format_unformat_error, line_input); } unformat_free (line_input); The 'else' returns if an unknown string is encountered. There a memory leak because the 'unformat_free(line_input)' is not called. There is a large number of instances of this pattern. Replaced the previous pattern with: /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "x")) x = 1; : else { error = clib_error_return (0, "unknown input `%U'", format_unformat_error, line_input); goto done: } } /* ...Remaining code... */ done: unformat_free (line_input); return error; } In multiple files, 'unformat_free (line_input);' was never called, so there was a memory leak whether an invalid string was entered or not. Also, there were multiple instance where: error = clib_error_return (0, "unknown input `%U'", format_unformat_error, line_input); used 'input' as the last parameter instead of 'line_input'. The result is that output did not contain the substring in error, instead just an empty string. Fixed all of those as well. There are a lot of file, and very mind numbing work, so tried to keep it to a pattern to avoid mistakes. Change-Id: I8902f0c32a47dd7fb3bb3471a89818571702f1d2 Signed-off-by: Billy McFall <bmcfall@redhat.com> Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-22jvpp: remove unnecessary msg_id_base cachingMarek Gradzki11-178/+90
Jvpp code uses CRCs to obtain msg IDs. Checking api_main_t.msg_index_by_name_and_crc is enough to detect API mismatch. Calling vl_client_get_first_plugin_msg_id is not needed. Also fixes VPP-627. Change-Id: Ie3085dfa458795fa11f17615ac94e76197a1c8cd Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
2017-02-16Consolidate DHCP v4 and V6 implementation. No functional change intendedNeale Ranns15-959/+850
The DHCP proxy and VSS information maintained by VPP is the same for v4 and v6, so we can manage this state using the same code. Packet handling is cleary different, so this is kept separate. Change-Id: I10f10cc1f7f19debcd4c4b099c6de64e56bb0c69 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-22fix trace frame-queue unformat of indexMatus Fabian1-1/+1
Change-Id: Id891af5ef3c4afe877282b34cd03fc43886940a3 Signed-off-by: Matus Fabian <matfabia@cisco.com>
2017-02-22Fix last run time update for timer wheelFlorin Coras1-1/+1
Change-Id: I9ac04b15440297c154ed1e3fba888915044cb245 Signed-off-by: Florin Coras <fcoras@cisco.com>
2017-02-22Repair SNAT's IPFIX and IF-add-del test functions.Jon Loeliger1-2/+2
Inspection shows that the names of two functions: api_snat_ipfix_enable_disable() api_snat_add_del_interface_addr() don't match their bodies and have been swapped. Make the world right again by swapping them to match. Change-Id: Ieefd7f0fdbf52794e8649b0cbbcf6e1403c1b90a Signed-off-by: Jon Loeliger <jdl@netgate.com>
2017-02-21Add Overlay Network Engine APIFilip Tehlar8-212/+4068
Change-Id: I6b5984df176688f0722a2888e73f05d8ed8b9310 Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
2017-02-21dhcp: multiple additionsNeale Ranns13-768/+892
DHCP additions: 1) DHCPv4 will only relay a message back to the client, if the Option82 information is present. So make this the default. 2) It is no longer possible to select via the API to "insert circuit ID" - since this is now default 3) Remove the version 2 API since it's now the same as version 1. 4) Adding the VSS option is now conditional only on the presence of VSS config (not the 'insert' option in the set API) 5) DHCP proxy dump via API Change-Id: Ia7271ba8c1d4dbf34a02c401d268ccfbb1b74f17 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-21cryptodev: Automatically download and build ISA-L Crypto libraryRadu Nicolau1-1/+1
Change-Id: I5454053461e6fb98e7f58f9562efde3590bb7cb5 Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
2017-02-21Rename LISP GPE API to GPEFilip Tehlar11-153/+147
Change-Id: I133c55bce46d40ffddabbbf8626cbd3d072522d4 Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
2017-02-21VPP-540 : pbb tag rewrite detailsPavel Kotucek7-82/+200
Extended sw_interface_dump to provide 802.1ah (pbb) tag rewrite info if present. Extended log "l2-output" to provide raw data to display result of prospetive pbb tag rewrite. Tracing is moved after l2output_vtr to show these changes. Change-Id: I8b7cb865dc67ce21afab402cc086dac35f7c0f07 Signed-off-by: Pavel Kotucek <pkotucek@cisco.com>
2017-02-20FIB reset leaves residual routes. Wrong API used to remove the routes meant ↵Neale Ranns2-2/+5
the lock count on the entry did not drop to zero Change-Id: I6e2dff8c3c7976fd1c2e4c5258f5dc73123aa9b7 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-20LISP: don't show PITR generated mapping in dump callFilip Tehlar4-3/+15
Change-Id: Iecba818ccf74a4d34e35d498e6f6a1d3c62419f4 Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
2017-02-20dpdk: updated build to automatically download Intel(R) Multi-Buffer Crypto ↵Radu Nicolau1-1/+1
for IPsec Library Change-Id: I58182edb7b0d314bb6dfa1daf7b00012196fd3e1 Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
2017-02-20CLI extension to add multiple (S,G)s at once and time itNeale Ranns1-10/+76
Change-Id: Id17060fd0e8ac80c8cf1999b0b82d0241b3b969a Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-20Python test IP and MPLS objects conform to infra.Neale Ranns12-15/+476
Add IP[46] MFIB dump. Change-Id: I4a2821f65e67a5416b291e4912c84f64989883b8 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-17Remove duplicate ip6 get interface address codeNeale Ranns4-47/+9
Change-Id: I5e0057b36bc4221e688a27fc1c0f602f78132991 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-17BFD: put session admin-up/admin-downKlement Sekera1-12/+12
Change-Id: I7d8889dce8495607106593ad83320c9af0f2fa07 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2017-02-17Implemented IKEv2 initiator features:Radu Nicolau14-186/+2534
- IKE_SA_INIT and IKE_AUTH initial exchanges - Delete IKA SA - Rekey and delete Child SA - Child SAs lifetime policy To set up one VPP instance as the initiator use the following CLI commands (or API equivalents): ikev2 profile set <id> responder <interface> <addr> ikev2 profile set <id> ike-crypto-alg <crypto alg> <key size> ike-integ-alg <integ alg> ike-dh <dh type> ikev2 profile set <id> esp-crypto-alg <crypto alg> <key size> esp-integ-alg <integ alg> esp-dh <dh type> ikev2 profile set <id> sa-lifetime <seconds> <jitter> <handover> <max bytes> and finally ikev2 initiate sa-init <profile id> to initiate the IKE_SA_INIT exchange Child SA re-keying process: 1. Child SA expires 2. A new Child SA is created using the Child SA rekey exchange 3. For a set time both SAs are alive 4. After the set time interval expires old SA is deleted Any additional settings will not be carried over (i.e. settings of the ipsec<x> interface associated with the Child SA) CLI API additions: ikev2 profile set <id> responder <interface> <addr> ikev2 profile set <id> ike-crypto-alg <crypto alg> <key size> ike-integ-alg <integ alg> ike-dh <dh type> ikev2 profile set <id> esp-crypto-alg <crypto alg> <key size> esp-integ-alg <integ alg> esp-dh <dh type> ikev2 profile set <id> sa-lifetime <seconds> <jitter> <handover> <max bytes> ikev2 initiate sa-init <profile id> ikev2 initiate del-child-sa <child sa ispi> ikev2 initiate del-sa <sa ispi> ikev2 initiate rekey-child-sa <profile id> <child sa ispi> Sample configurations: Responder: ikev2 profile add pr1 ikev2 profile set pr1 auth shared-key-mic string Vpp123 ikev2 profile set pr1 id local fqdn vpp.home.responder ikev2 profile set pr1 id remote fqdn vpp.home.initiator ikev2 profile set pr1 traffic-selector remote ip-range 192.168.125.0 - 192.168.125.255 port-range 0 - 65535 protocol 0 ikev2 profile set pr1 traffic-selector local ip-range 192.168.124.0 - 192.168.124.255 port-range 0 - 65535 protocol 0 Initiator: ikev2 profile add pr1 ikev2 profile set pr1 auth shared-key-mic string Vpp123 ikev2 profile set pr1 id local fqdn vpp.home.initiator ikev2 profile set pr1 id remote fqdn vpp.home.responder ikev2 profile set pr1 traffic-selector local ip-range 192.168.125.0 - 192.168.125.255 port-range 0 - 65535 protocol 0 ikev2 profile set pr1 traffic-selector remote ip-range 192.168.124.0 - 192.168.124.255 port-range 0 - 65535 protocol 0 ikev2 profile set pr1 responder TenGigabitEthernet3/0/1 192.168.40.20 ikev2 profile set pr1 ike-crypto-alg aes-cbc 192 ike-integ-alg sha1-96 ike-dh modp-2048 ikev2 profile set pr1 esp-crypto-alg aes-cbc 192 esp-integ-alg sha1-96 esp-dh ecp-256 ikev2 profile set pr1 sa-lifetime 3600 10 5 0 Change-Id: I1db9084dc787129ea61298223fb7585a6f7eaf9e Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
2017-02-17Fix handling of ping to SNAT out interfaceJuraj Sloboda1-6/+7
Change-Id: I322bfb3469b3d0d5b0cac39a6c2dba1c6f83ce3d Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
2017-02-17ipsec: changed ipsec-input-ip6 node to be a sibling of ipsec-input-ip4, ↵Radu Nicolau1-6/+1
fixes a problem that occurs with cryptodev ipv6 input. Change-Id: I1f0c0db45b2aabc243dd785c8d5d5ef990cac903 Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
2017-02-17l2 input: avoid per-packet trace checks in the fast pathDave Barach4-25/+54
Change-Id: Ib0c8572773499d8dd4d81b3a565c24412ccc3510 Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-17Fix comment for num-mbufs default in startup.confDave Wallace1-1/+1
Change-Id: I8bb175cc9673895d4a8856786ecabfd66dd906e9 Signed-off-by: Dave Wallace <dwallacelf@gmail.com>
2017-02-17dpdk: quad loop and prefetch in fill_free_listDamjan Marion1-15/+71
Change-Id: I19ec3b769b6512f7408044751393d9faf10d01d5 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-02-17ioam: declare export_node instead of defining it in header fileDamjan Marion1-1/+1
Change-Id: Ib1760312df759c29a2c2220e7b783af311d91d1a Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-02-17api: remove debug print in api_main_initDamjan Marion1-1/+0
Change-Id: I8f5cf447c131a790e4bbd46ef75063329fec7451 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-02-16tw_timer_expire_timers() - add a maximum to the number of expiration per callGabriel Ganne2-3/+10
The idea is to prevent a huge processing burst if, say, the network goes down 10' for some reason, and so that we don't need to expire 1M timer sessions on the first call. The maximum is not an exact value, but a value after which the expiration process is postponed until the next call. That way, we don't have to process the same tick twice, nor to unlink timers once at a time when processing a tick. The fact that a timer slot could contain many entries should be dealt with by changing the number of ticks per second. Change-Id: I892d07f965094102a3d53e7dbf4e6f5ad22d4967 Signed-off-by: Gabriel Ganne <gabriel.ganne@enea.com>
2017-02-16Add NSH load-balance and drop DPOFlorin Coras5-15/+148
Also adds missing gpe nsh address type functions. Change-Id: I3353a23c0518da9ce3b221ddf8c5bd0364930154 Signed-off-by: Florin Coras <fcoras@cisco.com>
2017-02-16Fix NSH-LISP interface additionFlorin Coras1-15/+16
Change-Id: I3925d2ebb2d26c676fc61f118d25bdf7fd522f26 Signed-off-by: Florin Coras <fcoras@cisco.com>
2017-02-16Fix crash on deleting previously activated IPv6 interface - VPP-636Wojciech Dec1-0/+4
RADV Pool index was not getting updated Change-Id: I2d2f14c56f51034d39049d1c7e13c248180a865f Signed-off-by: Wojciech Dec <wdec@cisco.com>
2017-02-16Fix sample plugin breakage.Anlu Yan3-23/+10
Add vat_helper_macros.h to be installed in /usr/include/vlibapi Define a version for the sample plugin (separate from the VPP versioning). Hook up vnet_main in plugin init. Change-Id: I293b9dc824d0813ea2bb8747d535e4210a88b385 Signed-off-by: Anlu Yan <ayan@cisco.com>
>index; } always_inline uword is_white_space (uword c) { switch (c) { case ' ': case '\t': case '\n': case '\r': return 1; default: return 0; } } /* Format function for dumping input stream. */ u8 * format_unformat_error (u8 * s, va_list * va) { unformat_input_t *i = va_arg (*va, unformat_input_t *); uword l = vec_len (i->buffer); /* Only show so much of the input buffer (it could be really large). */ uword n_max = 30; if (i->index < l) { uword n = l - i->index; u8 *p, *p_end; p = i->buffer + i->index; p_end = p + (n > n_max ? n_max : n); /* Skip white space at end. */ if (n <= n_max) { while (p_end > p && is_white_space (p_end[-1])) p_end--; } while (p < p_end) { switch (*p) { case '\r': vec_add (s, "\\r", 2); break; case '\n': vec_add (s, "\\n", 2); break; case '\t': vec_add (s, "\\t", 2); break; default: vec_add1 (s, *p); break; } p++; } if (n > n_max) vec_add (s, "...", 3); } return s; } /* Print everything: not just error context. */ u8 * format_unformat_input (u8 * s, va_list * va) { unformat_input_t *i = va_arg (*va, unformat_input_t *); uword l, n; if (i->index == UNFORMAT_END_OF_INPUT) s = format (s, "{END_OF_INPUT}"); else { l = vec_len (i->buffer); n = l - i->index; if (n > 0) vec_add (s, i->buffer + i->index, n); } return s; } #if CLIB_DEBUG > 0 void di (unformat_input_t * i) { fformat (stderr, "%U\n", format_unformat_input, i); } #endif /* Parse delimited vector string. If string starts with { then string is delimited by balenced parenthesis. Other string is delimited by white space. {} were chosen since they are special to the shell. */ static uword unformat_string (unformat_input_t * input, uword delimiter_character, uword format_character, va_list * va) { u8 **string_return = va_arg (*va, u8 **); u8 *s = 0; word paren = 0; word is_paren_delimited = 0; word backslash = 0; uword c; switch (delimiter_character) { case '%': case ' ': case '\t': delimiter_character = 0; break; } while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { word add_to_vector; /* Null return string means to skip over delimited input. */ add_to_vector = string_return != 0; if (backslash) backslash = 0; else switch (c) { case '\\': backslash = 1; add_to_vector = 0; break; case '{': if (paren == 0 && vec_len (s) == 0) { is_paren_delimited = 1; add_to_vector = 0; } paren++; break; case '}': paren--; if (is_paren_delimited && paren == 0) goto done; break; case ' ': case '\t': case '\n': case '\r': if (!is_paren_delimited) { unformat_put_input (input); goto done; } break; default: if (!is_paren_delimited && c == delimiter_character) { unformat_put_input (input); goto done; } } if (add_to_vector) vec_add1 (s, c); } done: if (string_return) { /* Match the string { END-OF-INPUT as a single brace. */ if (c == UNFORMAT_END_OF_INPUT && vec_len (s) == 0 && paren == 1) vec_add1 (s, '{'); /* Don't match null string. */ if (c == UNFORMAT_END_OF_INPUT && vec_len (s) == 0) return 0; /* Null terminate C string. */ if (format_character == 's') vec_add1 (s, 0); *string_return = s; } else vec_free (s); /* just to make sure */ return 1; } uword unformat_hex_string (unformat_input_t * input, va_list * va) { u8 **hexstring_return = va_arg (*va, u8 **); u8 *s; uword n, d, c; n = 0; d = 0; s = 0; while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { if (c >= '0' && c <= '9') d = 16 * d + c - '0'; else if (c >= 'a' && c <= 'f') d = 16 * d + 10 + c - 'a'; else if (c >= 'A' && c <= 'F') d = 16 * d + 10 + c - 'A'; else { unformat_put_input (input); break; } n++; if (n == 2) { vec_add1 (s, d); n = d = 0; } } /* Hex string must have even number of digits. */ if (n % 2) { vec_free (s); return 0; } /* Make sure something was processed. */ else if (s == 0) { return 0; } *hexstring_return = s; return 1; } /* unformat (input "foo%U", unformat_eof) matches terminal foo only */ uword unformat_eof (unformat_input_t * input, va_list * va) { return unformat_check_input (input) == UNFORMAT_END_OF_INPUT; } /* Parse a token containing given set of characters. */ uword unformat_token (unformat_input_t * input, va_list * va) { u8 *token_chars = va_arg (*va, u8 *); u8 **string_return = va_arg (*va, u8 **); u8 *s, map[256]; uword i, c; if (!token_chars) token_chars = (u8 *) "a-zA-Z0-9_"; memset (map, 0, sizeof (map)); for (s = token_chars; *s;) { /* Parse range. */ if (s[0] < s[2] && s[1] == '-') { for (i = s[0]; i <= s[2]; i++) map[i] = 1; s = s + 3; } else { map[s[0]] = 1; s = s + 1; } } s = 0; while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { if (!map[c]) { unformat_put_input (input); break; } vec_add1 (s, c); } if (vec_len (s) == 0) return 0; *string_return = s; return 1; } /* Unformat (parse) function which reads a %s string and converts it to and unformat_input_t. */ uword unformat_input (unformat_input_t * i, va_list * args) { unformat_input_t *sub_input = va_arg (*args, unformat_input_t *); u8 *s; if (unformat (i, "%v", &s)) { unformat_init_vector (sub_input, s); return 1; } return 0; } /* Parse a line ending with \n and return it. */ uword unformat_line (unformat_input_t * i, va_list * va) { u8 *line = 0, **result = va_arg (*va, u8 **); uword c; while ((c = unformat_get_input (i)) != '\n' && c != UNFORMAT_END_OF_INPUT) { vec_add1 (line, c); } *result = line; return 1; } /* Parse a line ending with \n and return it as an unformat_input_t. */ uword unformat_line_input (unformat_input_t * i, va_list * va) { unformat_input_t *result = va_arg (*va, unformat_input_t *); u8 *line; unformat_user (i, unformat_line, &line); unformat_init_vector (result, line); return 1; } /* Values for is_signed. */ #define UNFORMAT_INTEGER_SIGNED 1 #define UNFORMAT_INTEGER_UNSIGNED 0 static uword unformat_integer (unformat_input_t * input, va_list * va, uword base, uword is_signed, uword data_bytes) { uword c, digit; uword value = 0; uword n_digits = 0; uword n_input = 0; uword sign = 0; /* We only support bases <= 64. */ if (base < 2 || base > 64) goto error; while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { switch (c) { case '-': if (n_input == 0) { if (is_signed) { sign = 1; goto next_digit; } else /* Leading sign for unsigned number. */ goto error; } /* Sign after input (e.g. 100-200). */ goto put_input_done; case '+': if (n_input > 0) goto put_input_done; sign = 0; goto next_digit; case '0' ... '9': digit = c - '0'; break; case 'a' ... 'z': digit = 10 + (c - 'a'); break; case 'A' ... 'Z': digit = 10 + (base >= 36 ? 26 : 0) + (c - 'A'); break; case '/': digit = 62; break; case '?': digit = 63; break; default: goto put_input_done; } if (digit >= base) { put_input_done: unformat_put_input (input); goto done; } { uword new_value = base * value + digit; /* Check for overflow. */ if (new_value < value) goto error; value = new_value; } n_digits += 1; next_digit: n_input++; } done: if (sign) value = -value; if (n_digits > 0) { void *v = va_arg (*va, void *); if (data_bytes == ~0) data_bytes = sizeof (int); switch (data_bytes) { case 1: *(u8 *) v = value; break; case 2: *(u16 *) v = value; break; case 4: *(u32 *) v = value; break; case 8: *(u64 *) v = value; break; default: goto error; } return 1; } error: return 0; } /* Return x 10^n */ static f64 times_power_of_ten (f64 x, int n) { if (n >= 0) { static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, }; while (n >= 8) { x *= 1e+8; n -= 8; } return x * t[n]; } else { static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, }; while (n <= -8) { x *= 1e-8; n += 8; } return x * t[-n]; } } static uword unformat_float (unformat_input_t * input, va_list * va) { uword c; u64 values[3]; uword n_digits[3], value_index = 0; uword signs[2], sign_index = 0; uword n_input = 0; memset (values, 0, sizeof (values)); memset (n_digits, 0, sizeof (n_digits)); memset (signs, 0, sizeof (signs)); while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { switch (c) { case '-': if (value_index == 2 && n_digits[2] == 0) /* sign of exponent: it's ok. */ ; else if (value_index < 2 && n_digits[0] > 0) { /* 123- */ unformat_put_input (input); goto done; } else if (n_input > 0) goto error; signs[sign_index++] = 1; goto next_digit; case '+': if (value_index == 2 && n_digits[2] == 0) /* sign of exponent: it's ok. */ ; else if (value_index < 2 && n_digits[0] > 0) { /* 123+ */ unformat_put_input (input); goto done; } else if (n_input > 0) goto error; signs[sign_index++] = 0; goto next_digit; case 'e': case 'E': if (n_input == 0) goto error; value_index = 2; sign_index = 1; break; case '.': if (value_index > 0) goto error; value_index = 1; break; case '0' ... '9': { u64 tmp; tmp = values[value_index] * 10 + c - '0'; /* Check for overflow. */ if (tmp < values[value_index]) goto error; values[value_index] = tmp; n_digits[value_index] += 1; } break; default: unformat_put_input (input); goto done; } next_digit: n_input++; } done: { f64 f_values[2], *value_return; word expon; /* Must have either whole or fraction digits. */ if (n_digits[0] + n_digits[1] <= 0) goto error; f_values[0] = values[0]; if (signs[0]) f_values[0] = -f_values[0]; f_values[1] = values[1]; f_values[1] = times_power_of_ten (f_values[1], -n_digits[1]); f_values[0] += f_values[1]; expon = values[2]; if (signs[1]) expon = -expon; f_values[0] = times_power_of_ten (f_values[0], expon); value_return = va_arg (*va, f64 *); *value_return = f_values[0]; return 1; } error: return 0; } static const char * match_input_with_format (unformat_input_t * input, const char *f) { uword cf, ci; ASSERT (*f != 0); while (1) { cf = *f; if (cf == 0 || cf == '%' || cf == ' ') break; f++; ci = unformat_get_input (input); if (cf != ci) return 0; } return f; } static const char * do_percent (unformat_input_t * input, va_list * va, const char *f) { uword cf, n, data_bytes = ~0; cf = *f++; switch (cf) { default: break; case 'w': /* Word types. */ cf = *f++; data_bytes = sizeof (uword); break; case 'l': cf = *f++; if (cf == 'l') { cf = *f++; data_bytes = sizeof (long long); } else { data_bytes = sizeof (long); } break; case 'L': cf = *f++; data_bytes = sizeof (long long); break; } n = 0; switch (cf) { case 'D': data_bytes = va_arg (*va, int); case 'd': n = unformat_integer (input, va, 10, UNFORMAT_INTEGER_SIGNED, data_bytes); break; case 'u': n = unformat_integer (input, va, 10, UNFORMAT_INTEGER_UNSIGNED, data_bytes); break; case 'b': n = unformat_integer (input, va, 2, UNFORMAT_INTEGER_UNSIGNED, data_bytes); break; case 'o': n = unformat_integer (input, va, 8, UNFORMAT_INTEGER_UNSIGNED, data_bytes); break; case 'X': data_bytes = va_arg (*va, int); case 'x': n = unformat_integer (input, va, 16, UNFORMAT_INTEGER_UNSIGNED, data_bytes); break; case 'f': n = unformat_float (input, va); break; case 's': case 'v': n = unformat_string (input, f[0], cf, va); break; case 'U': { unformat_function_t *f = va_arg (*va, unformat_function_t *); n = f (input, va); } break; case '=': case '|': { int *var = va_arg (*va, int *); uword val = va_arg (*va, int); if (cf == '|') val |= *var; *var = val; n = 1; } break; } return n ? f : 0; } uword unformat_skip_white_space (unformat_input_t * input) { uword n = 0; uword c; while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { if (!is_white_space (c)) { unformat_put_input (input); break; } n++; } return n; } uword va_unformat (unformat_input_t * input, const char *fmt, va_list * va) { const char *f; uword input_matches_format; uword default_skip_input_white_space; uword n_input_white_space_skipped; uword last_non_white_space_match_percent; uword last_non_white_space_match_format; vec_add1_aligned (input->buffer_marks, input->index, sizeof (input->buffer_marks[0])); f = fmt; default_skip_input_white_space = 1; input_matches_format = 0; last_non_white_space_match_percent = 0; last_non_white_space_match_format = 0; while (1) { char cf; uword is_percent, skip_input_white_space; cf = *f; is_percent = 0; /* Always skip input white space at start of format string. Otherwise use default skip value which can be changed by %_ (see below). */ skip_input_white_space = f == fmt || default_skip_input_white_space; /* Spaces in format request skipping input white space. */ if (is_white_space (cf)) { skip_input_white_space = 1; /* Multiple format spaces are equivalent to a single white space. */ while (is_white_space (*++f)) ; } else if (cf == '%') { /* %_ toggles whether or not to skip input white space. */ switch (*++f) { case '_': default_skip_input_white_space = !default_skip_input_white_space; f++; /* For transition from skip to no-skip in middle of format string, skip input white space. For example, the following matches: fmt = "%_%d.%d%_->%_%d.%d%_" input "1.2 -> 3.4" Without this the space after -> does not get skipped. */ if (!default_skip_input_white_space && !(f == fmt + 2 || *f == 0)) unformat_skip_white_space (input); continue; /* %% means match % */ case '%': break; /* % at end of format string. */ case 0: goto parse_fail; default: is_percent = 1; break; } } n_input_white_space_skipped = 0; if (skip_input_white_space) n_input_white_space_skipped = unformat_skip_white_space (input); /* End of format string. */ if (cf == 0) { /* Force parse error when format string ends and input is not white or at end. As an example, this is to prevent format "foo" from matching input "food". The last_non_white_space_match_percent is to make "foo %d" match input "foo 10,bletch" with %d matching 10. */ if (skip_input_white_space && !last_non_white_space_match_percent && !last_non_white_space_match_format && n_input_white_space_skipped == 0 && input->index != UNFORMAT_END_OF_INPUT) goto parse_fail; break; } last_non_white_space_match_percent = is_percent; last_non_white_space_match_format = 0; /* Explicit spaces in format must match input white space. */ if (cf == ' ' && !default_skip_input_white_space) { if (n_input_white_space_skipped == 0) goto parse_fail; } else if (is_percent) { if (!(f = do_percent (input, va, f))) goto parse_fail; } else { const char *g = match_input_with_format (input, f); if (!g) goto parse_fail; last_non_white_space_match_format = g > f; f = g; } } input_matches_format = 1; parse_fail: /* Rewind buffer marks. */ { uword l = vec_len (input->buffer_marks); /* If we did not match back up buffer to last mark. */ if (!input_matches_format) input->index = input->buffer_marks[l - 1]; _vec_len (input->buffer_marks) = l - 1; } return input_matches_format; } uword unformat (unformat_input_t * input, const char *fmt, ...) { va_list va; uword result; va_start (va, fmt); result = va_unformat (input, fmt, &va); va_end (va); return result; } uword unformat_user (unformat_input_t * input, unformat_function_t * func, ...) { va_list va; uword result, l; /* Save place in input buffer in case parse fails. */ l = vec_len (input->buffer_marks); vec_add1_aligned (input->buffer_marks, input->index, sizeof (input->buffer_marks[0])); va_start (va, func); result = func (input, &va); va_end (va); if (!result && input->index != UNFORMAT_END_OF_INPUT) input->index = input->buffer_marks[l]; _vec_len (input->buffer_marks) = l; return result; } /* Setup for unformat of Unix style command line. */ void unformat_init_command_line (unformat_input_t * input, char *argv[]) { uword i; unformat_init (input, 0, 0); /* Concatenate argument strings with space in between. */ for (i = 1; argv[i]; i++) { vec_add (input->buffer, argv[i], strlen (argv[i])); if (argv[i + 1]) vec_add1 (input->buffer, ' '); } } void unformat_init_string (unformat_input_t * input, char *string, int string_len) { unformat_init (input, 0, 0); if (string_len > 0) vec_add (input->buffer, string, string_len); } void unformat_init_vector (unformat_input_t * input, u8 * vector_string) { unformat_init (input, 0, 0); input->buffer = vector_string; } #ifdef CLIB_UNIX static uword unix_file_fill_buffer (unformat_input_t * input) { int fd = pointer_to_uword (input->fill_buffer_arg); uword l, n; l = vec_len (input->buffer); vec_resize (input->buffer, 4096); n = read (fd, input->buffer + l, 4096); if (n > 0) _vec_len (input->buffer) = l + n; if (n <= 0) return UNFORMAT_END_OF_INPUT; else return input->index; } void unformat_init_unix_file (unformat_input_t * input, int file_descriptor) { unformat_init (input, unix_file_fill_buffer, uword_to_pointer (file_descriptor, void *)); } /* Take input from Unix environment variable. */ uword unformat_init_unix_env (unformat_input_t * input, char *var) { char *val = getenv (var); if (val) unformat_init_string (input, val, strlen (val)); return val != 0; } #endif /* CLIB_UNIX */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */