diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /network_cmds | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'network_cmds')
154 files changed, 65374 insertions, 0 deletions
diff --git a/network_cmds/APPLE_LICENSE b/network_cmds/APPLE_LICENSE new file mode 100644 index 0000000..e7aa7d0 --- /dev/null +++ b/network_cmds/APPLE_LICENSE @@ -0,0 +1,370 @@ + APPLE PUBLIC SOURCE LICENSE + Version 1.0 - March 16, 1999 + +Please read this License carefully before downloading this software. +By downloading and using this software, you are agreeing to be bound +by the terms of this License. If you do not or cannot agree to the +terms of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other + work which Apple Computer, Inc. ("Apple") publicly announces as + subject to this Apple Public Source License and which contains a + notice placed by Apple identifying such program or work as "Original + Code" and stating that it is subject to the terms of this Apple + Public Source License version 1.0 (or subsequent version thereof), + as it may be revised from time to time by Apple ("License"). As + used in this License: + +1.1 "Applicable Patents" mean: (a) in the case where Apple is the + grantor of rights, (i) patents or patent applications that are now + or hereafter acquired, owned by or assigned to Apple and (ii) whose + claims cover subject matter contained in the Original Code, but only + to the extent necessary to use, reproduce and/or distribute the + Original Code without infringement; and (b) in the case where You + are the grantor of rights, (i) patents and patent applications that + are now or hereafter acquired, owned by or assigned to You and (ii) + whose claims cover subject matter in Your Modifications, taken alone + or in combination with Original Code. + +1.2 "Covered Code" means the Original Code, Modifications, the + combination of Original Code and any Modifications, and/or any + respective portions thereof. + +1.3 "Deploy" means to use, sublicense or distribute Covered Code other + than for Your internal research and development (R&D), and includes + without limitation, any and all internal use or distribution of + Covered Code within Your business or organization except for R&D + use, as well as direct or indirect sublicensing or distribution of + Covered Code by You to any third party in any form or manner. + +1.4 "Larger Work" means a work which combines Covered Code or portions + thereof with code not governed by the terms of this License. + +1.5 "Modifications" mean any addition to, deletion from, and/or change + to, the substance and/or structure of Covered Code. When code is + released as a series of files, a Modification is: (a) any addition + to or deletion from the contents of a file containing Covered Code; + and/or (b) any new file or other representation of computer program + statements that contains any part of Covered Code. + +1.6 "Original Code" means the Source Code of a program or other work + as originally made available by Apple under this License, including + the Source Code of any updates or upgrades to such programs or works + made available by Apple under this License, and that has been + expressly identified by Apple as such in the header file(s) of such + work. + +1.7 "Source Code" means the human readable form of a program or other + work that is suitable for making modifications to it, including all + modules it contains, plus any associated interface definition files, + scripts used to control compilation and installation of an + executable (object code). + +1.8 "You" or "Your" means an individual or a legal entity exercising + rights under this License. For legal entities, "You" or "Your" + includes any entity which controls, is controlled by, or is under + common control with, You, where "control" means (a) the power, + direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (b) ownership of fifty + percent (50%) or more of the outstanding shares or beneficial + ownership of such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms + and conditions of this License, Apple hereby grants You, effective + on the date You accept this License and download the Original Code, + a world-wide, royalty-free, non-exclusive license, to the extent of + Apple's Applicable Patents and copyrights covering the Original + Code, to do the following: + +2.1 You may use, copy, modify and distribute Original Code, with or + without Modifications, solely for Your internal research and + development, provided that You must in each instance: + +(a) retain and reproduce in all copies of Original Code the copyright +and other proprietary notices and disclaimers of Apple as they appear +in the Original Code, and keep intact all notices in the Original Code +that refer to this License; + +(b) include a copy of this License with every copy of Source Code of +Covered Code and documentation You distribute, and You may not offer +or impose any terms on such Source Code that alter or restrict this +License or the recipients' rights hereunder, except as permitted under +Section 6; and + +(c) completely and accurately document all Modifications that you have +made and the date of each such Modification, designate the version of +the Original Code you used, prominently include a file carrying such +information with the Modifications, and duplicate the notice in +Exhibit A in each file of the Source Code of all such Modifications. + +2.2 You may Deploy Covered Code, provided that You must in each + instance: + +(a) satisfy all the conditions of Section 2.1 with respect to the +Source Code of the Covered Code; + +(b) make all Your Deployed Modifications publicly available in Source +Code form via electronic distribution (e.g. download from a web site) +under the terms of this License and subject to the license grants set +forth in Section 3 below, and any additional terms You may choose to +offer under Section 6. You must continue to make the Source Code of +Your Deployed Modifications available for as long as you Deploy the +Covered Code or twelve (12) months from the date of initial +Deployment, whichever is longer; + +(c) must notify Apple and other third parties of how to obtain Your +Deployed Modifications by filling out and submitting the required +information found at +http://www.apple.com/publicsource/modifications.html; and + +(d) if you Deploy Covered Code in object code, executable form only, +include a prominent notice, in the code itself as well as in related +documentation, stating that Source Code of the Covered Code is +available under the terms of this License with information on how and +where to obtain such Source Code. + +3. Your Grants. In consideration of, and as a condition to, the + licenses granted to You under this License: + +(a) You hereby grant to Apple and all third parties a non-exclusive, +royalty-free license, under Your Applicable Patents and other +intellectual property rights owned or controlled by You, to use, +reproduce, modify, distribute and Deploy Your Modifications of the +same scope and extent as Apple's licenses under Sections 2.1 and 2.2; +and + +(b) You hereby grant to Apple and its subsidiaries a non-exclusive, +worldwide, royalty-free, perpetual and irrevocable license, under Your +Applicable Patents and other intellectual property rights owned or +controlled by You, to use, reproduce, execute, compile, display, +perform, modify or have modified (for Apple and/or its subsidiaries), +sublicense and distribute Your Modifications, in any form, through +multiple tiers of distribution. + +4. Larger Works. You may create a Larger Work by combining Covered + Code with other code not governed by the terms of this License and + distribute the Larger Work as a single product. In each such + instance, You must make sure the requirements of this License are + fulfilled for the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in + Section 2, no other patent rights, express or implied, are granted + by Apple herein. Modifications and/or Larger Works may require + additional patent licenses from Apple which Apple may grant in its + sole discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee + for, warranty, support, indemnity or liability obligations and/or + other rights consistent with the scope of the license granted herein + ("Additional Terms") to one or more recipients of Covered + Code. However, You may do so only on Your own behalf and as Your + sole responsibility, and not on behalf of Apple. You must obtain the + recipient's agreement that any such Additional Terms are offered by + You alone, and You hereby agree to indemnify, defend and hold Apple + harmless for any liability incurred by or claims asserted against + Apple by reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new + versions of this License from time to time. Each version will be + given a distinguishing version number. Once Original Code has been + published under a particular version of this License, You may + continue to use it under the terms of that version. You may also + choose to use such Original Code under the terms of any subsequent + version of this License published by Apple. No one other than Apple + has the right to modify the terms applicable to Covered Code created + under this License. + +8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or + in part pre-release, untested, or not fully tested works. The + Original Code may contain errors that could cause failures or loss + of data, and may be incomplete or contain inaccuracies. You + expressly acknowledge and agree that use of the Original Code, or + any portion thereof, is at Your sole and entire risk. THE ORIGINAL + CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT + OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF + SECTIONS 8 AND 9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY + REFERRED TO AS "APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR + CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY OR + SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE DOES NOT WARRANT THAT + THE FUNCTIONS CONTAINED IN THE ORIGINAL CODE WILL MEET YOUR + REQUIREMENTS, OR THAT THE OPERATION OF THE ORIGINAL CODE WILL BE + UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE ORIGINAL CODE + WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN + BY APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE SHALL CREATE A + WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. You + acknowledge that the Original Code is not intended for use in the + operation of nuclear facilities, aircraft navigation, communication + systems, or air traffic control machines in which case the failure + of the Original Code could lead to death, personal injury, or severe + physical or environmental damage. + +9. Liability. + +9.1 Infringement. If any of the Original Code becomes the subject of + a claim of infringement ("Affected Original Code"), Apple may, at + its sole discretion and option: (a) attempt to procure the rights + necessary for You to continue using the Affected Original Code; (b) + modify the Affected Original Code so that it is no longer + infringing; or (c) terminate Your rights to use the Affected + Original Code, effective immediately upon Apple's posting of a + notice to such effect on the Apple web site that is used for + implementation of this License. + +9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE + LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR + INABILITY TO USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER + UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), + PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF + ESSENTIAL PURPOSE OF ANY REMEDY. In no event shall Apple's total + liability to You for all damages under this License exceed the + amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the + trademarks or trade names "Apple", "Apple Computer", "Mac OS X", + "Mac OS X Server" or any other trademarks or trade names belonging + to Apple (collectively "Apple Marks") and no Apple Marks may be + used to endorse or promote products derived from the Original Code + other than as permitted by and in strict compliance at all times + with Apple's third party trademark usage guidelines which are + posted at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Apple retains all rights, title and interest in and to + the Original Code and any Modifications made by or on behalf of + Apple ("Apple Modifications"), and such Apple Modifications will + not be automatically subject to this License. Apple may, at its + sole discretion, choose to license such Apple Modifications under + this License, or on different terms from those contained in this + License or may choose not to license them at all. Apple's + development, use, reproduction, modification, sublicensing and + distribution of Covered Code will not be subject to this License. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will + terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; (b) immediately in the event of +the circumstances described in Sections 9.1 and/or 13.6(b); or (c) +automatically without notice from Apple if You, at any time during the +term of this License, commence an action for patent infringement +against Apple. + +12.2 Effect of Termination. Upon termination, You agree to + immediately stop any further use, reproduction, modification and + distribution of the Covered Code, or Affected Original Code in the + case of termination under Section 9.1, and to destroy all copies of + the Covered Code or Affected Original Code (in the case of + termination under Section 9.1) that are in your possession or + control. All sublicenses to the Covered Code which have been + properly granted prior to termination shall survive any termination + of this License. Provisions which, by their nature, should remain + in effect beyond the termination of this License shall survive, + including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and + 13. Neither party will be liable to the other for compensation, + indemnity or damages of any sort solely as a result of terminating + this License in accordance with its terms, and termination of this + License will be without prejudice to any other right or remedy of + either party. + +13. Miscellaneous. + +13.1 Export Law Assurances. You may not use or otherwise export or + re-export the Original Code except as authorized by United States + law and the laws of the jurisdiction in which the Original Code was + obtained. In particular, but without limitation, the Original Code + may not be exported or re-exported (a) into (or to a national or + resident of) any U.S. embargoed country or (b) to anyone on the + U.S. Treasury Department's list of Specially Designated Nationals + or the U.S. Department of Commerce's Table of Denial Orders. By + using the Original Code, You represent and warrant that You are not + located in, under control of, or a national or resident of any such + country or on any such list. + +13.2 Government End Users. The Covered Code is a "commercial item" as + defined in FAR 2.101. Government software and technical data + rights in the Covered Code include only those rights customarily + provided to the public as defined in this License. This customary + commercial license in technical data and software is provided in + accordance with FAR 12.211 (Technical Data) and 12.212 (Computer + Software) and, for Department of Defense purchases, DFAR + 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 + (Rights in Commercial Computer Software or Computer Software + Documentation). Accordingly, all U.S. Government End Users acquire + Covered Code with only those rights set forth herein. + +13.3 Relationship of Parties. This License will not be construed as + creating an agency, partnership, joint venture or any other form of + legal association between You and Apple, and You will not represent + to the contrary, whether expressly, by implication, appearance or + otherwise. + +13.4 Independent Development. Nothing in this License will impair + Apple's right to acquire, license, develop, have others develop for + it, market and/or distribute technology or products that perform + the same or similar functions as, or otherwise compete with, + Modifications, Larger Works, technology or products that You may + develop, produce, market or distribute. + +13.5 Waiver; Construction. Failure by Apple to enforce any provision + of this License will not be deemed a waiver of future enforcement + of that or any other provision. Any law or regulation which + provides that the language of a contract shall be construed against + the drafter will not apply to this License. + +13.6 Severability. (a) If for any reason a court of competent + jurisdiction finds any provision of this License, or portion + thereof, to be unenforceable, that provision of the License will be + enforced to the maximum extent permissible so as to effect the + economic benefits and intent of the parties, and the remainder of + this License will continue in full force and effect. (b) + Notwithstanding the foregoing, if applicable law prohibits or + restricts You from fully and/or specifically complying with + Sections 2 and/or 3 or prevents the enforceability of either of + those Sections, this License will immediately terminate and You + must immediately discontinue any use of the Covered Code and + destroy all copies of it that are in your possession or control. + +13.7 Dispute Resolution. Any litigation or other dispute resolution + between You and Apple relating to this License shall take place in + the Northern District of California, and You and Apple hereby + consent to the personal jurisdiction of, and venue in, the state + and federal courts within that District with respect to this + License. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly + excluded. + +13.8 Entire Agreement; Governing Law. This License constitutes the + entire agreement between the parties with respect to the subject + matter hereof. This License shall be governed by the laws of the + United States and the State of California, except that body of + California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights +Reserved. This file contains Original Code and/or Modifications of +Original Code as defined in and that are subject to the Apple Public +Source License Version 1.0 (the 'License'). You may not use this file +except in compliance with the License. Please obtain a copy of the +License at http://www.apple.com/publicsource and read it before using +this file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the +License for the specific language governing rights and limitations +under the License." diff --git a/network_cmds/arp.tproj/IMPORT_NOTES b/network_cmds/arp.tproj/IMPORT_NOTES new file mode 100644 index 0000000..231c5ca --- /dev/null +++ b/network_cmds/arp.tproj/IMPORT_NOTES @@ -0,0 +1,3 @@ +arp.c - FreeBSD file, included types.h, socket.h, ethernet.h, removed + token ring header, removed __unused__ attributes, hid token ring code + with #ifndef __APPLE__ diff --git a/network_cmds/arp.tproj/arp.8 b/network_cmds/arp.tproj/arp.8 new file mode 100644 index 0000000..b586c26 --- /dev/null +++ b/network_cmds/arp.tproj/arp.8 @@ -0,0 +1,251 @@ +.\" Copyright (c) 2012 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)arp.8 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.sbin/arp/arp.8,v 1.25.2.1 2008/04/25 16:38:14 sam Exp $ +.\" +.Dd March 18, 2008 +.Dt ARP 8 +.Os +.Sh NAME +.Nm arp +.Nd address resolution display and control +.Sh SYNOPSIS +.Nm +.Op Fl n +.Op Fl i Ar interface +.Ar hostname +.Nm +.Op Fl n +.Op Fl i Ar interface +.Op Fl l +.Fl a +.Nm +.Fl d Ar hostname +.Op Cm pub +.Op Cm ifscope interface +.Nm +.Fl d +.Op Fl i Ar interface +.Fl a +.Nm +.Fl s Ar hostname ether_addr +.Op Cm temp +.Op Cm reject +.Op Cm blackhole +.Op Cm pub Op Cm only +.Op Cm ifscope interface +.Nm +.Fl S Ar hostname ether_addr +.Op Cm temp +.Op Cm reject +.Op Cm blackhole +.Op Cm pub Op Cm only +.Op Cm ifscope interface +.Nm +.Fl f Ar filename +.Sh DESCRIPTION +The +.Nm +utility displays and modifies the Internet-to-Ethernet address translation +tables used by the address resolution protocol +.Pq Xr arp 4 . +With no flags, the program displays the current +.Tn ARP +entry for +.Ar hostname . +The host may be specified by name or by number, +using Internet dot notation. +.Pp +Available options: +.Bl -tag -width indent +.It Fl a +The program displays or deletes all of the current +.Tn ARP +entries. +.It Fl d +A super-user may delete an entry for the host called +.Ar hostname +with the +.Fl d +flag. +If the +.Cm pub +keyword is specified, only the +.Dq published +.Tn ARP +entry +for this host will be deleted. +If the +.Cm ifscope +keyword is specified, the entry specific to the interface will be deleted. +.Pp +Alternatively, the +.Fl d +flag may be combined with the +.Fl a +flag to delete all entries. +.It Fl i Ar interface +Limit the operation scope to the +.Tn ARP +entries on +.Ar interface . +Applicable only to the following operations: +display one, display all, delete all. +.It Fl l +Show link-layer reachability information. +.It Fl n +Show network addresses as numbers (normally +.Nm +attempts to display addresses symbolically). +.It Fl s Ar hostname ether_addr +Create an +.Tn ARP +entry for the host called +.Ar hostname +with the Ethernet address +.Ar ether_addr . +The Ethernet address is given as six hex bytes separated by colons. +The entry will be permanent unless the word +.Cm temp +is given in the command. +If the word +.Cm pub +is given, the entry will be +.Dq published ; +i.e., this system will +act as an +.Tn ARP +server, +responding to requests for +.Ar hostname +even though the host address is not its own. +In this case the +.Ar ether_addr +can be given as +.Cm auto +in which case the interfaces on this host will be examined, +and if one of them is found to occupy the same subnet, its +Ethernet address will be used. +If the +.Cm only +keyword is also specified, this will create a +.Dq "published (proxy only)" +entry. +This type of entry is created automatically if +.Nm +detects that a routing table entry for +.Ar hostname +already exists. +.Pp +If the +.Cm reject +keyword is specified the entry will be marked so that traffic to +the host will be discarded and the sender will be notified the +host is unreachable. +The +.Cm blackhole +keyword is similar in that traffic is discarded but the sender is +not notified. +These can be used to block external traffic to a host without +using a firewall. +.Pp +If the +.Cm ifscope +keyword is specified, the entry will set with an additional property that +strictly associate the entry to the interface. This allows +for the presence of mutiple entries with the same destination +on different interfaces. +.It Fl S Ar hostname ether_addr +Is just like +.Fl s +except any existing +.Tn ARP +entry for this host will be deleted first. +.It Fl f Ar filename +Cause the file +.Ar filename +to be read and multiple entries to be set in the +.Tn ARP +tables. +Entries +in the file should be of the form +.Pp +.Bd -ragged -offset indent -compact +.Ar hostname ether_addr +.Op Cm temp +.Op Cm pub Op Cm only +.Op Cm ifscope interface +.Ed +.Pp +with argument meanings as given above. +Leading whitespace and empty lines are ignored. +A +.Ql # +character will mark the rest of the line as a comment. +.It Fl x +Show extended link-layer reachability information in addition to that shown by +the +.Fl l +flag. +.El +.Sh SEE ALSO +.Xr inet 3 , +.Xr arp 4 , +.Xr ifconfig 8 , +.Xr ndp 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.3 . diff --git a/network_cmds/arp.tproj/arp.c b/network_cmds/arp.tproj/arp.c new file mode 100644 index 0000000..eeca003 --- /dev/null +++ b/network_cmds/arp.tproj/arp.c @@ -0,0 +1,1163 @@ +/* + * Copyright (c) 2003-2012 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Sun Microsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1984, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ +#endif + +/* + * arp - display, set, and delete arp table entries + */ + + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> +#if 0 +#include <net/iso88025.h> +#endif + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <arpa/inet.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +typedef void (action_fn)(struct sockaddr_dl *sdl, + struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); +typedef void (action_ext_fn)(struct sockaddr_dl *sdl, + struct sockaddr_inarp *s_in, struct rt_msghdr_ext *rtm); + +static int search(in_addr_t addr, action_fn *action); +static int search_ext(in_addr_t addr, action_ext_fn *action); +static action_fn print_entry; +static action_fn nuke_entry; +static action_ext_fn print_entry_ext; + +static char *print_lladdr(struct sockaddr_dl *); +static int delete(char *host, int do_proxy); +static void usage(void); +static int set(int argc, char **argv); +static int get(char *host); +static int file(char *name); +static struct rt_msghdr *rtmsg(int cmd, + struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); +static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); +static struct sockaddr_inarp *getaddr(char *host); +static int valid_type(int type); +static char *sec2str(time_t); + +static int nflag; /* no reverse dns lookups */ +static int xflag; /* extended link-layer reachability information */ +static char *rifname; + +static int expire_time, flags, doing_proxy, proxy_only; + +static char *boundif = NULL; +static unsigned int ifscope = 0; + +/* which function we're supposed to do */ +#define F_GET 1 +#define F_SET 2 +#define F_FILESET 3 +#define F_REPLACE 4 +#define F_DELETE 5 + +#ifndef SA_SIZE +#define SA_SIZE(sa) \ + ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ + sizeof(uint32_t) : \ + 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) ) +#endif + +#define SETFUNC(f) { if (func) usage(); func = (f); } + + +int +main(int argc, char *argv[]) +{ + int ch, func = 0; + int rtn = 0; + int aflag = 0; /* do it for all entries */ + int lflag = 0; + uint32_t ifindex = 0; + + while ((ch = getopt(argc, argv, "andflsSi:x")) != -1) + switch((char)ch) { + case 'a': + aflag = 1; + break; + case 'd': + SETFUNC(F_DELETE); + break; + case 'n': + nflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'S': + SETFUNC(F_REPLACE); + break; + case 's': + SETFUNC(F_SET); + break; + case 'f' : + SETFUNC(F_FILESET); + break; + case 'i': + rifname = optarg; + break; + case 'x': + xflag = 1; + lflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!func) + func = F_GET; + if (rifname) { + if (func != F_GET && !(func == F_DELETE && aflag)) + errx(1, "-i not applicable to this operation"); + if ((ifindex = if_nametoindex(rifname)) == 0) { + if (errno == ENXIO) + errx(1, "interface %s does not exist", rifname); + else + err(1, "if_nametoindex(%s)", rifname); + } + } + switch (func) { + case F_GET: + if (aflag) { + if (argc != 0) + usage(); + if (lflag) { + printf("%-23s %-17s %-9.9s %-9.9s %8.8s %4s " + "%4s", "Neighbor", + "Linklayer Address", "Expire(O)", + "Expire(I)", "Netif", "Refs", "Prbs"); + if (xflag) + printf(" %-7.7s %-7.7s %-7.7s", + "RSSI", "LQM", "NPM"); + printf("\n"); + search_ext(0, print_entry_ext); + } else { + search(0, print_entry); + } + } else { + if (argc != 1) + usage(); + rtn = get(argv[0]); + } + break; + case F_SET: + case F_REPLACE: + if (argc < 2 || argc > 6) + usage(); + if (func == F_REPLACE) + (void)delete(argv[0], 0); + rtn = set(argc, argv) ? 1 : 0; + break; + case F_DELETE: + if (aflag) { + if (argc != 0) + usage(); + search(0, nuke_entry); + } else { + int do_proxy = 0; + int i; + + for (i = 1; i < argc; i++) { + if (strncmp(argv[i], "pub", sizeof("pub")) == 0) { + do_proxy = SIN_PROXY; + } else if (strncmp(argv[i], "ifscope", sizeof("ifscope")) == 0) { + if (i + 1 >= argc) { + printf("ifscope needs an interface parameter\n"); + return (1); + } + boundif = argv[++i]; + if ((ifscope = if_nametoindex(boundif)) == 0) + errx(1, "ifscope has bad interface name: %s", boundif); + } else { + usage(); + } + } + if (i > argc) + usage(); + rtn = delete(argv[0], do_proxy); + } + break; + case F_FILESET: + if (argc != 1) + usage(); + rtn = file(argv[0]); + break; + } + + return (rtn); +} + +/* + * Process a file to set standard arp entries + */ +static int +file(char *name) +{ + FILE *fp; + int i, retval; + char line[128], arg[7][50], *args[7], *p; + + if ((fp = fopen(name, "r")) == NULL) + err(1, "cannot open %s", name); + args[0] = &arg[0][0]; + args[1] = &arg[1][0]; + args[2] = &arg[2][0]; + args[3] = &arg[3][0]; + args[4] = &arg[4][0]; + args[5] = &arg[5][0]; + args[6] = &arg[6][0]; + retval = 0; + while(fgets(line, sizeof(line), fp) != NULL) { + if ((p = strchr(line, '#')) != NULL) + *p = '\0'; + for (p = line; isblank(*p); p++); + if (*p == '\n' || *p == '\0') + continue; + i = sscanf(p, "%49s %49s %49s %49s %49s %49s %49s", arg[0], arg[1], + arg[2], arg[3], arg[4], arg[5], arg[6]); + if (i < 2) { + warnx("bad line: %s", line); + retval = 1; + continue; + } + if (set(i, args)) + retval = 1; + } + fclose(fp); + return (retval); +} + +/* + * Given a hostname, fills up a (static) struct sockaddr_inarp with + * the address of the host and returns a pointer to the + * structure. + */ +static struct sockaddr_inarp * +getaddr(char *host) +{ + struct hostent *hp; + static struct sockaddr_inarp reply; + + bzero(&reply, sizeof(reply)); + reply.sin_len = sizeof(reply); + reply.sin_family = AF_INET; + reply.sin_addr.s_addr = inet_addr(host); + if (reply.sin_addr.s_addr == INADDR_NONE) { + if (!(hp = gethostbyname(host))) { + warnx("%s: %s", host, hstrerror(h_errno)); + return (NULL); + } + bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, + sizeof reply.sin_addr); + } + return (&reply); +} + +/* + * Returns true if the type is a valid one for ARP. + */ +static int +valid_type(int type) +{ + + switch (type) { + case IFT_ETHER: + case IFT_FDDI: + case IFT_ISO88023: + case IFT_ISO88024: +#if 0 + case IFT_ISO88025: +#endif + case IFT_L2VLAN: +#ifdef IFT_BRIDGE + case IFT_BRIDGE: +#endif + return (1); + default: + return (0); + } +} + +/* + * Set an individual arp entry + */ +static int +set(int argc, char **argv) +{ + struct sockaddr_inarp *addr; + struct sockaddr_inarp *dst; /* what are we looking for */ + struct sockaddr_dl *sdl; + struct rt_msghdr *rtm; + struct ether_addr *ea; + char *host = argv[0], *eaddr = argv[1]; + struct sockaddr_dl sdl_m; + + argc -= 2; + argv += 2; + + bzero(&sdl_m, sizeof(sdl_m)); + sdl_m.sdl_len = sizeof(sdl_m); + sdl_m.sdl_family = AF_LINK; + + dst = getaddr(host); + if (dst == NULL) + return (1); + doing_proxy = flags = proxy_only = expire_time = 0; + boundif = NULL; + ifscope = 0; + while (argc-- > 0) { + if (strncmp(argv[0], "temp", sizeof("temp")) == 0) { + struct timeval tv; + gettimeofday(&tv, 0); + expire_time = tv.tv_sec + 20 * 60; + } else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) { + flags |= RTF_ANNOUNCE; + doing_proxy = 1; + if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) { + proxy_only = 1; + dst->sin_other = SIN_PROXY; + argc--; argv++; + } + } else if (strncmp(argv[0], "blackhole", sizeof("blackhole")) == 0) { + flags |= RTF_BLACKHOLE; + } else if (strncmp(argv[0], "reject", sizeof("reject")) == 0) { + flags |= RTF_REJECT; + } else if (strncmp(argv[0], "trail", sizeof("trail")) == 0) { + /* XXX deprecated and undocumented feature */ + printf("%s: Sending trailers is no longer supported\n", + host); + } else if (strncmp(argv[0], "ifscope", sizeof("ifscope")) == 0) { + if (argc < 1) { + printf("ifscope needs an interface parameter\n"); + return (1); + } + boundif = argv[1]; + if ((ifscope = if_nametoindex(boundif)) == 0) + errx(1, "ifscope has bad interface name: %s", boundif); + argc--; argv++; + } + argv++; + } + ea = (struct ether_addr *)LLADDR(&sdl_m); + if (doing_proxy && !strcmp(eaddr, "auto")) { + if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { + printf("no interface found for %s\n", + inet_ntoa(dst->sin_addr)); + return (1); + } + sdl_m.sdl_alen = ETHER_ADDR_LEN; + } else { + struct ether_addr *ea1 = ether_aton(eaddr); + + if (ea1 == NULL) { + warnx("invalid Ethernet address '%s'", eaddr); + return (1); + } else { + *ea = *ea1; + sdl_m.sdl_alen = ETHER_ADDR_LEN; + } + } + for (;;) { /* try at most twice */ + rtm = rtmsg(RTM_GET, dst, &sdl_m); + if (rtm == NULL) { + warn("%s", host); + return (1); + } + addr = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); + if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) + break; + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY) && + valid_type(sdl->sdl_type) ) + break; + /* + * If we asked for a scope entry and did not get one or + * did not asked for a scope entry and got one, we can + * proceed. + */ + if ((ifscope != 0) != (rtm->rtm_flags & RTF_IFSCOPE)) + break; + if (doing_proxy == 0) { + printf("set: can only proxy for %s\n", host); + return (1); + } + if (dst->sin_other & SIN_PROXY) { + printf("set: proxy entry exists for non 802 device\n"); + return (1); + } + dst->sin_other = SIN_PROXY; + proxy_only = 1; + } + + if (sdl->sdl_family != AF_LINK) { + printf("cannot intuit interface index and type for %s\n", host); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); +} + +/* + * Display an individual arp entry + */ +static int +get(char *host) +{ + struct sockaddr_inarp *addr; + + addr = getaddr(host); + if (addr == NULL) + return (1); + if (0 == search(addr->sin_addr.s_addr, print_entry)) { + printf("%s (%s) -- no entry", + host, inet_ntoa(addr->sin_addr)); + if (rifname) + printf(" on %s", rifname); + printf("\n"); + return (1); + } + return (0); +} + +/* + * Delete an arp entry + */ +static int +delete(char *host, int do_proxy) +{ + struct sockaddr_inarp *addr, *dst; + struct rt_msghdr *rtm; + struct sockaddr_dl *sdl; + + dst = getaddr(host); + if (dst == NULL) + return (1); + dst->sin_other = do_proxy; + for (;;) { /* try twice */ + rtm = rtmsg(RTM_GET, dst, NULL); + if (rtm == NULL) { + warn("%s", host); + return (1); + } + addr = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); + if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && + sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY) && + valid_type(sdl->sdl_type) ) + break; /* found it */ + if (dst->sin_other & SIN_PROXY) { + fprintf(stderr, "delete: cannot locate %s\n",host); + return (1); + } + dst->sin_other = SIN_PROXY; + } + if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { + printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); + return (0); + } + return (1); +} + +/* + * Search the arp table and do some action on matching entries + */ +static int +search(in_addr_t addr, action_fn *action) +{ + int mib[6]; + size_t needed; + char *lim, *buf, *newbuf, *next; + struct rt_msghdr *rtm; + struct sockaddr_inarp *sin2; + struct sockaddr_dl *sdl; + char ifname[IF_NAMESIZE]; + int st, found_entry = 0; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(1, "route-sysctl-estimate"); + if (needed == 0) /* empty table */ + return 0; + buf = NULL; + for (;;) { + newbuf = realloc(buf, needed); + if (newbuf == NULL) { + if (buf != NULL) + free(buf); + errx(1, "could not reallocate memory"); + } + buf = newbuf; + st = sysctl(mib, 6, buf, &needed, NULL, 0); + if (st == 0 || errno != ENOMEM) + break; + needed += needed / 8; + } + if (st == -1) + err(1, "actual retrieval of routing table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sin2 = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); + if (rifname && if_indextoname(sdl->sdl_index, ifname) && + strcmp(ifname, rifname)) + continue; + if (addr) { + if (addr != sin2->sin_addr.s_addr) + continue; + found_entry = 1; + } + (*action)(sdl, sin2, rtm); + } + free(buf); + return (found_entry); +} + +/* + * Stolen and adapted from ifconfig + */ +static char * +print_lladdr(struct sockaddr_dl *sdl) +{ + static char buf[256]; + char *cp; + int n, bufsize = sizeof (buf), p = 0; + + bzero(buf, sizeof (buf)); + cp = (char *)LLADDR(sdl); + if ((n = sdl->sdl_alen) > 0) { + while (--n >= 0) + p += snprintf(buf + p, bufsize - p, "%x%s", + *cp++ & 0xff, n > 0 ? ":" : ""); + } + return (buf); +} + +/* + * Display an arp entry + */ +static void +print_entry(struct sockaddr_dl *sdl, + struct sockaddr_inarp *addr, struct rt_msghdr *rtm) +{ + const char *host; + struct hostent *hp; + char ifname[IF_NAMESIZE]; +#if 0 + struct iso88025_sockaddr_dl_data *trld; + int seg; +#endif + + if (nflag == 0) + hp = gethostbyaddr((caddr_t)&(addr->sin_addr), + sizeof addr->sin_addr, AF_INET); + else + hp = 0; + if (hp) + host = hp->h_name; + else { + host = "?"; + if (h_errno == TRY_AGAIN) + nflag = 1; + } + printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); + if (sdl->sdl_alen) { +#if 1 + printf("%s", print_lladdr(sdl)); +#else + if ((sdl->sdl_type == IFT_ETHER || + sdl->sdl_type == IFT_L2VLAN || + sdl->sdl_type == IFT_BRIDGE) && + sdl->sdl_alen == ETHER_ADDR_LEN) + printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); + else { + int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; + + printf("%s", link_ntoa(sdl) + n); + } +#endif + } else + printf("(incomplete)"); + if (if_indextoname(sdl->sdl_index, ifname) != NULL) + printf(" on %s", ifname); + if ((rtm->rtm_flags & RTF_IFSCOPE)) + printf(" ifscope"); + if (rtm->rtm_rmx.rmx_expire == 0) + printf(" permanent"); + if (addr->sin_other & SIN_PROXY) + printf(" published (proxy only)"); + if (rtm->rtm_addrs & RTA_NETMASK) { + addr = (struct sockaddr_inarp *) + (SA_SIZE(sdl) + (char *)sdl); + if (addr->sin_addr.s_addr == 0xffffffff) + printf(" published"); + if (addr->sin_len != 8) + printf("(weird)"); + } + switch(sdl->sdl_type) { + case IFT_ETHER: + printf(" [ethernet]"); + break; +#if 0 + case IFT_ISO88025: + printf(" [token-ring]"); + trld = SDL_ISO88025(sdl); + if (trld->trld_rcf != 0) { + printf(" rt=%x", ntohs(trld->trld_rcf)); + for (seg = 0; + seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); + seg++) + printf(":%x", ntohs(*(trld->trld_route[seg]))); + } + break; +#endif + case IFT_FDDI: + printf(" [fddi]"); + break; + case IFT_ATM: + printf(" [atm]"); + break; + case IFT_L2VLAN: + printf(" [vlan]"); + break; + case IFT_IEEE1394: + printf(" [firewire]"); + break; +#ifdef IFT_BRIDGE + case IFT_BRIDGE: + printf(" [bridge]"); + break; +#endif + default: + break; + } + + printf("\n"); + +} + +/* + * Nuke an arp entry + */ +static void +nuke_entry(struct sockaddr_dl *sdl __unused, + struct sockaddr_inarp *addr, struct rt_msghdr *rtm) +{ + char ip[20]; + + snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); + /* + * When deleting all entries, specify the interface scope of each entry + */ + if ((rtm->rtm_flags & RTF_IFSCOPE)) + ifscope = rtm->rtm_index; + (void)delete(ip, 0); + ifscope = 0; +} + +static void +usage(void) +{ + fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + "usage: arp [-n] [-i interface] hostname", + " arp [-n] [-i interface] [-l] -a", + " arp -d hostname [pub] [ifscope interface]", + " arp -d [-i interface] -a", + " arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", + " arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", + " arp -f filename"); + exit(1); +} + +static struct rt_msghdr * +rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) +{ + static int seq; + int rlen; + int l; + struct sockaddr_in so_mask, *so_mask_ptr = &so_mask; + static int s = -1; + static pid_t pid; + + static struct { + struct rt_msghdr m_rtm; + char m_space[512]; + } m_rtmsg; + + struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + char *cp = m_rtmsg.m_space; + + if (s < 0) { /* first time: open socket, get pid */ + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + err(1, "socket"); + pid = getpid(); + } + bzero(&so_mask, sizeof(so_mask)); + so_mask.sin_len = 8; + so_mask.sin_addr.s_addr = 0xffffffff; + + errno = 0; + /* + * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer + * appropriately (except for the mask set just above). + */ + if (cmd == RTM_DELETE) + goto doit; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + /* + * Note: On RTM_GET the kernel will return a scoped route when both a scoped route and + * a unscoped route exist. That means we cannot delete a unscoped route if there is + * also a matching scope route + */ + if (ifscope) { + rtm->rtm_index = ifscope; + rtm->rtm_flags |= RTF_IFSCOPE; + } + + switch (cmd) { + default: + errx(1, "internal wrong cmd"); + case RTM_ADD: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); + dst->sin_other = 0; + if (doing_proxy) { + if (proxy_only) + dst->sin_other = SIN_PROXY; + else { + rtm->rtm_addrs |= RTA_NETMASK; + rtm->rtm_flags &= ~RTF_HOST; + } + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if ((s) != NULL && rtm->rtm_addrs & (w)) { \ + bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);} + + NEXTADDR(RTA_DST, dst); + NEXTADDR(RTA_GATEWAY, sdl); + NEXTADDR(RTA_NETMASK, so_mask_ptr); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; +doit: + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if (errno != ESRCH || cmd != RTM_DELETE) { + warn("writing to routing socket"); + return (NULL); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); + if (l < 0) + warn("read from routing socket"); + return (rtm); +} + +/* + * get_ether_addr - get the hardware address of an interface on the + * the same subnet as ipaddr. + */ +#define MAX_IFS 32 + +static int +get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) +{ + struct ifreq *ifr, *ifend, *ifp; + in_addr_t ina, mask; + struct sockaddr_dl *dla; + struct ifreq ifreq; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + int sock; + int retval = 0; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + err(1, "socket"); + + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + warnx("ioctl(SIOCGIFCONF)"); + goto done; + } + +#define NEXTIFR(i) \ + ((struct ifreq *)((char *)&(i)->ifr_addr \ + + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) + + /* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + strlcpy(ifreq.ifr_name, ifr->ifr_name, + sizeof(ifreq.ifr_name)); + ifreq.ifr_addr = ifr->ifr_addr; + /* + * Check that the interface is up, + * and not point-to-point or loopback. + */ + if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) + continue; + if ((ifreq.ifr_flags & + (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| + IFF_LOOPBACK|IFF_NOARP)) + != (IFF_UP|IFF_BROADCAST)) + continue; + /* + * Get its netmask and check that it's on + * the right subnet. + */ + if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) + continue; + mask = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr.s_addr; + ina = ((struct sockaddr_in *) + &ifr->ifr_addr)->sin_addr.s_addr; + if ((ipaddr & mask) == (ina & mask)) + break; /* ok, we got it! */ + } + + if (ifr >= ifend) + goto done; + + /* + * Now scan through again looking for a link-level address + * for this interface. + */ + ifp = ifr; + for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) + if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && + ifr->ifr_addr.sa_family == AF_LINK) + break; + if (ifr >= ifend) + goto done; + /* + * Found the link-level address - copy it out + */ + dla = (struct sockaddr_dl *) &ifr->ifr_addr; + memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); + printf("using interface %s for proxy with address ", + ifp->ifr_name); + printf("%s\n", ether_ntoa(hwaddr)); + retval = dla->sdl_alen; +done: + close(sock); + return (retval); +} + +static char * +sec2str(total) + time_t total; +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + + return(result); +} + +static int +search_ext(in_addr_t addr, action_ext_fn *action) +{ + int mib[6]; + size_t needed; + char *lim, *buf, *newbuf, *next; + struct rt_msghdr_ext *ertm; + struct sockaddr_inarp *sin2; + struct sockaddr_dl *sdl; + char ifname[IF_NAMESIZE]; + int st, found_entry = 0; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_DUMPX_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(1, "route-sysctl-estimate"); + if (needed == 0) /* empty table */ + return 0; + buf = NULL; + for (;;) { + newbuf = realloc(buf, needed); + if (newbuf == NULL) { + if (buf != NULL) + free(buf); + errx(1, "could not reallocate memory"); + } + buf = newbuf; + st = sysctl(mib, 6, buf, &needed, NULL, 0); + if (st == 0 || errno != ENOMEM) + break; + needed += needed / 8; + } + if (st == -1) + err(1, "actual retrieval of routing table"); + lim = buf + needed; + for (next = buf; next < lim; next += ertm->rtm_msglen) { + ertm = (struct rt_msghdr_ext *)next; + sin2 = (struct sockaddr_inarp *)(ertm + 1); + sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); + if (rifname && if_indextoname(sdl->sdl_index, ifname) && + strcmp(ifname, rifname)) + continue; + if (addr) { + if (addr != sin2->sin_addr.s_addr) + continue; + found_entry = 1; + } + (*action)(sdl, sin2, ertm); + } + free(buf); + return (found_entry); +} + +static void +print_entry_ext(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr, + struct rt_msghdr_ext *ertm) +{ + const char *host; + struct hostent *hp; + char ifname[IF_NAMESIZE]; + struct timeval time; + + if (nflag == 0) + hp = gethostbyaddr((caddr_t)&(addr->sin_addr), + sizeof (addr->sin_addr), AF_INET); + else + hp = 0; + + if (hp) + host = hp->h_name; + else + host = inet_ntoa(addr->sin_addr); + + printf("%-23s ", host); + + if (sdl->sdl_alen) + printf("%-17s ", print_lladdr(sdl)); + else + printf("%-17s ", "(incomplete)"); + + gettimeofday(&time, 0); + + if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_snd_expire == 0) + printf("%-9.9s ", "(none)"); + else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec) + printf("%-9.9s ", + sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec)); + else + printf("%-9.9s ", "expired"); + + if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_rcv_expire == 0) + printf("%-9.9s", "(none)"); + else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec) + printf("%-9.9s", + sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec)); + else + printf("%-9.9s", "expired"); + + if (if_indextoname(sdl->sdl_index, ifname) == NULL) + snprintf(ifname, sizeof (ifname), "%s", "?"); + printf(" %8.8s", ifname); + + if (ertm->rtm_ri.ri_refcnt) { + printf(" %4d", ertm->rtm_ri.ri_refcnt); + if (ertm->rtm_ri.ri_probes) + printf(" %4d", ertm->rtm_ri.ri_probes); + + if (xflag) { + if (!ertm->rtm_ri.ri_probes) + printf(" %-4.4s", "none"); + + if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN) + printf(" %7d", ertm->rtm_ri.ri_rssi); + else + printf(" %-7.7s", "unknown"); + + switch (ertm->rtm_ri.ri_lqm) + { + case IFNET_LQM_THRESH_OFF: + printf(" %-7.7s", "off"); + break; + case IFNET_LQM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_LQM_THRESH_POOR: + printf(" %-7.7s", "poor"); + break; + case IFNET_LQM_THRESH_GOOD: + printf(" %-7.7s", "good"); + break; + default: + printf(" %7d", ertm->rtm_ri.ri_lqm); + break; + } + + switch (ertm->rtm_ri.ri_npm) + { + case IFNET_NPM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_NPM_THRESH_NEAR: + printf(" %-7.7s", "near"); + break; + case IFNET_NPM_THRESH_GENERAL: + printf(" %-7.7s", "general"); + break; + case IFNET_NPM_THRESH_FAR: + printf(" %-7.7s", "far"); + break; + default: + printf(" %7d", ertm->rtm_ri.ri_npm); + break; + } + } + } + printf("\n"); +} diff --git a/network_cmds/arp.tproj/arp4.4 b/network_cmds/arp.tproj/arp4.4 new file mode 100644 index 0000000..a8bb6e7 --- /dev/null +++ b/network_cmds/arp.tproj/arp4.4 @@ -0,0 +1,123 @@ +.\" Copyright (c) 1985, 1986, 1988, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)arp4.4 6.5 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt ARP 4 +.Os BSD 4 +.Sh NAME +.Nm arp +.Nd Address Resolution Protocol +.Sh SYNOPSIS +.Em "pseudo-device ether" +.Sh DESCRIPTION +The Address Resolution Protocol (ARP) is a protocol used to dynamically +map between Internet host addresses and 10Mb/s Ethernet addresses. +It is used by all the 10Mb/s Ethernet interface drivers. +It is not specific to Internet protocols or to 10Mb/s Ethernet, +but this implementation currently supports only that combination. +.Pp +ARP caches Internet-Ethernet address mappings. +When an interface requests a mapping for an address not in the cache, +ARP queues the message which requires the mapping and broadcasts +a message on the associated network requesting the address mapping. +If a response is provided, the new mapping is cached and any pending +message is transmitted. +ARP will queue at most one packet while waiting for a response to a +mapping request; +only the most recently ``transmitted'' packet is kept. +If the target host does not respond after several requests, +the host is considered to be down for a short period (normally 20 seconds), +allowing an error to be returned to transmission attempts during this +interval. +The error is +.Li EHOSTDOWN +for a non-responding destination host, and +.Li EHOSTUNREACH +for a non-responding router. +.Pp +The ARP cache is stored in the system routing table as +dynamically-created host routes. +The route to a directly-attached Ethernet network is installed as a +.Dq cloning +route (one with the +.Li RTF_CLONING +flag set), +causing routes to individual hosts on that network to be created on +demand. +These routes time out periodically (normally 20 minutes after validated; +entries are not validated when not in use). +An entry for a host which is not responding is a +.Dq reject +route (one with the +.Li RTF_REJECT +flag set). +.Pp +ARP entries may be added, deleted or changed with the +.Xr arp 8 +utility. +Manually-added entries may be temporary or permanent, +and may be +.Dq published , +in which case the system will respond to ARP requests for that host +as if it were the target of the request. +.Pp +In the past, +ARP was used to negotiate the use of a trailer encapsulation. +This is no longer supported. +.Pp +ARP watches passively for hosts impersonating the local host (i.e. a host +which responds to an ARP mapping request for the local host's address). +.Sh DIAGNOSTICS +.Em "duplicate IP address %x!! sent from ethernet address: %x:%x:%x:%x:%x:%x." +ARP has discovered another host on the local network which responds to +mapping requests for its own Internet address with a different Ethernet +address, generally indicating that two hosts are attempting to use the +same Internet address. +.Sh SEE ALSO +.Xr inet 4 , +.Xr route 4 , +.Xr arp 8 , +.Xr ifconfig 8 , +.Xr route 8 +.sp +.Rs +.%A Plummer, D. +.%B "An Ethernet Address Resolution Protocol" +.%T RFC826 +.Re +.Rs +.%A Leffler, S.J. +.%A Karels, M.J. +.%B "Trailer Encapsulations +.%T RFC893 +.Re diff --git a/network_cmds/cfilutil/cfilstat.c b/network_cmds/cfilutil/cfilstat.c new file mode 100644 index 0000000..a012e06 --- /dev/null +++ b/network_cmds/cfilutil/cfilstat.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include <sys/errno.h> +#include <sys/sysctl.h> +#include <net/content_filter.h> +#include <libproc.h> +#include <stdio.h> +#include <stdlib.h> +#include <err.h> +#include <unistd.h> +#include <string.h> + +#define IPPROTOCOL_TCP 6 +#define IPPROTOCOL_UDP 17 + +void +print_filter_list() +{ + size_t total_len, curr_len; + void *buffer = NULL; + void *ptr; + uint32_t line = 0; + + if (sysctlbyname("net.cfil.filter_list", NULL, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.filter_list)"); + + buffer = malloc(total_len); + if (buffer == NULL) + err(1, "malloc()"); + if (sysctlbyname("net.cfil.filter_list", buffer, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.filter_list)"); + + ptr = buffer; + curr_len = 0; + do { + struct cfil_filter_stat *filter_stat; + + filter_stat = (struct cfil_filter_stat *)ptr; + + if (curr_len + filter_stat->cfs_len > total_len || + filter_stat->cfs_len < sizeof(struct cfil_filter_stat)) + break; + + if (line % 16 == 0) + printf("%10s %10s %10s %10s\n", + "filter", "flags", "count", "necpunit"); + + printf("%10u 0x%08x %10u %10u\n", + filter_stat->cfs_filter_id, + filter_stat->cfs_flags, + filter_stat->cfs_sock_count, + filter_stat->cfs_necp_control_unit); + + ptr += filter_stat->cfs_len; + curr_len += filter_stat->cfs_len; + } while (1); + + free(buffer); +} + +void +sprint_offset(char *str, size_t len, const char *fmt, uint64_t offset) +{ + if (offset == CFM_MAX_OFFSET) + snprintf(str, len, "%s", "MAX"); + else + snprintf(str, len, fmt, offset); +} + +void +print_socket_list() +{ + size_t total_len, curr_len; + void *buffer = NULL; + void *ptr; + int i; + + if (sysctlbyname("net.cfil.sock_list", NULL, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.sock_list)"); + + buffer = malloc(total_len); + if (buffer == NULL) + err(1, "malloc()"); + if (sysctlbyname("net.cfil.sock_list", buffer, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.sock_list)"); + + ptr = buffer; + curr_len = 0; + do { + struct cfil_sock_stat *sock_stat; + char opass[32]; + char ipass[32]; + char namebuffer[256]; + char *procName = "<not found>"; + + sock_stat = (struct cfil_sock_stat *)ptr; + + if (curr_len + sock_stat->cfs_len > total_len || + sock_stat->cfs_len < sizeof(struct cfil_sock_stat)) + break; + + if (proc_name(sock_stat->cfs_e_pid, namebuffer, sizeof(namebuffer)) > 0) { + procName = namebuffer; + } + + sprint_offset(opass, 32, "%8llu", sock_stat->cfs_snd.cbs_pass_offset); + sprint_offset(ipass, 32, "%8llu", sock_stat->cfs_rcv.cbs_pass_offset); + + printf("%16s %5s %10s " + "%8s %8s %8s %8s %8s %8s %8s " + "%8s %8s %8s %8s %8s %8s %8s " + "%8s %8s %15s\n", + "sockid", "proto", "flags", + "ofirst", "olast", "oqlen", " ", "opass", " ", " ", + "ifirst", "ilast", "iqlen", " ", "ipass", " ", " ", + "pid", "epid", "eprocname"); + + printf("%016llu %5s 0x%08llx " + "%8llu %8llu %8llu %8s %8s %8s %8s " + "%8llu %8llu %8llu %8s %8s %8s %8s " + "%8u %8u %15s\n", + + sock_stat->cfs_sock_id, + sock_stat->cfs_sock_protocol == IPPROTOCOL_TCP ? "TCP" : "UDP", + sock_stat->cfs_flags, + + sock_stat->cfs_snd.cbs_pending_first, + sock_stat->cfs_snd.cbs_pending_last, + sock_stat->cfs_snd.cbs_inject_q_len, + " ", + opass, + " ", + " ", + + sock_stat->cfs_rcv.cbs_pending_first, + sock_stat->cfs_rcv.cbs_pending_last, + sock_stat->cfs_rcv.cbs_inject_q_len, + " ", + ipass, + " ", + " ", + sock_stat->cfs_pid, + sock_stat->cfs_e_pid, + procName); + + + printf("%7s %10s %10s " + "%8s %8s %8s %8s %8s %8s %8s " + "%8s %8s %8s %8s %8s %8s %8s\n", + " ", + "filter", "flags", + "octlfrst", "octllast", "opndfrst", "opndlast", "opass", "opked", "opeek", + "ictlfrst", "ictllast", "ipndfrst", "ipndlast", "ipass", "ipked", "ipeek"); + for (i = 0; i < CFIL_MAX_FILTER_COUNT; i++) { + struct cfil_entry_stat *estat; + char spass[32]; + char speek[32]; + char spked[32]; + char rpass[32]; + char rpeek[32]; + char rpked[32]; + + estat = &sock_stat->ces_entries[i]; + + sprint_offset(spass, 32, "%8llu", estat->ces_snd.cbs_pass_offset); + sprint_offset(speek, 32, "%8llu", estat->ces_snd.cbs_peek_offset); + sprint_offset(spked, 32, "%8llu", estat->ces_snd.cbs_peeked); + + sprint_offset(rpass, 32, "%8llu", estat->ces_rcv.cbs_pass_offset); + sprint_offset(rpeek, 32, "%8llu", estat->ces_rcv.cbs_peek_offset); + sprint_offset(rpked, 32, "%8llu", estat->ces_rcv.cbs_peeked); + + printf("%7s %10u 0x%08x " + "%8llu %8llu %8llu %8llu %8s %8s %8s " + "%8llu %8llu %8llu %8llu %8s %8s %8s\n", + + " ", + estat->ces_filter_id, + estat->ces_flags, + + estat->ces_snd.cbs_ctl_first, + estat->ces_snd.cbs_ctl_last, + estat->ces_snd.cbs_pending_first, + estat->ces_snd.cbs_pending_last, + spass, + spked, + speek, + + estat->ces_rcv.cbs_ctl_first, + estat->ces_rcv.cbs_ctl_last, + estat->ces_rcv.cbs_pending_first, + estat->ces_rcv.cbs_pending_last, + rpass, + rpked, + rpeek); + } + + + ptr += sock_stat->cfs_len; + curr_len += sock_stat->cfs_len; + } while (1); + + free(buffer); +} + + +#define PR32(x) printf(#x " %u\n", stats-> x) +#define PR64(x) printf(#x " %llu\n", stats-> x) +void +print_cfil_stats() +{ + size_t len, alloc_len; + void *buffer = NULL; + struct cfil_stats *stats; + + if (sysctlbyname("net.cfil.stats", NULL, &len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.stats)"); + + if (len < sizeof(struct cfil_stats)) + alloc_len = sizeof(struct cfil_stats); + else + alloc_len = len; + + buffer = malloc(alloc_len); + if (buffer == NULL) + err(1, "malloc()"); + if (sysctlbyname("net.cfil.stats", buffer, &len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.stats)"); + stats = (struct cfil_stats *)buffer; + + PR32(cfs_ctl_connect_ok); + PR32(cfs_ctl_connect_fail); + PR32(cfs_ctl_connect_ok); + PR32(cfs_ctl_connect_fail); + PR32(cfs_ctl_disconnect_ok); + PR32(cfs_ctl_disconnect_fail); + PR32(cfs_ctl_send_ok); + PR32(cfs_ctl_send_bad); + PR32(cfs_ctl_rcvd_ok); + PR32(cfs_ctl_rcvd_bad); + PR32(cfs_ctl_rcvd_flow_lift); + PR32(cfs_ctl_action_data_update); + PR32(cfs_ctl_action_drop); + PR32(cfs_ctl_action_bad_op); + PR32(cfs_ctl_action_bad_len); + + PR32(cfs_sock_id_not_found); + + PR32(cfs_cfi_alloc_ok); + PR32(cfs_cfi_alloc_fail); + + PR32(cfs_sock_userspace_only); + PR32(cfs_sock_attach_in_vain); + PR32(cfs_sock_attach_already); + PR32(cfs_sock_attach_no_mem); + PR32(cfs_sock_attach_failed); + PR32(cfs_sock_attached); + PR32(cfs_sock_detached); + + PR32(cfs_attach_event_ok); + PR32(cfs_attach_event_flow_control); + PR32(cfs_attach_event_fail); + + PR32(cfs_closed_event_ok); + PR32(cfs_closed_event_flow_control); + PR32(cfs_closed_event_fail); + + PR32(cfs_data_event_ok); + PR32(cfs_data_event_flow_control); + PR32(cfs_data_event_fail); + + PR32(cfs_disconnect_in_event_ok); + PR32(cfs_disconnect_out_event_ok); + PR32(cfs_disconnect_event_flow_control); + PR32(cfs_disconnect_event_fail); + + PR32(cfs_ctl_q_not_started); + + PR32(cfs_close_wait); + PR32(cfs_close_wait_timeout); + + PR32(cfs_flush_in_drop); + PR32(cfs_flush_out_drop); + PR32(cfs_flush_in_close); + PR32(cfs_flush_out_close); + PR32(cfs_flush_in_free); + PR32(cfs_flush_out_free); + + PR32(cfs_inject_q_nomem); + PR32(cfs_inject_q_nobufs); + PR32(cfs_inject_q_detached); + PR32(cfs_inject_q_in_fail); + PR32(cfs_inject_q_out_fail); + + PR32(cfs_inject_q_in_retry); + PR32(cfs_inject_q_out_retry); + + PR32(cfs_data_in_control); + PR32(cfs_data_in_oob); + PR32(cfs_data_out_control); + PR32(cfs_data_out_oob); + + PR64(cfs_ctl_q_in_enqueued); + PR64(cfs_ctl_q_out_enqueued); + PR64(cfs_ctl_q_in_peeked); + PR64(cfs_ctl_q_out_peeked); + + PR64(cfs_pending_q_in_enqueued); + PR64(cfs_pending_q_out_enqueued); + + PR64(cfs_inject_q_in_enqueued); + PR64(cfs_inject_q_out_enqueued); + PR64(cfs_inject_q_in_passed); + PR64(cfs_inject_q_out_passed); +} diff --git a/network_cmds/cfilutil/cfilutil.1 b/network_cmds/cfilutil/cfilutil.1 new file mode 100644 index 0000000..0d97adf --- /dev/null +++ b/network_cmds/cfilutil/cfilutil.1 @@ -0,0 +1,56 @@ +.Dd 2/10/14
+.Dt cfilutil 1
+.Os Darwin
+.Sh NAME
+.Nm cfilutil
+.Nd Tool to exercise the content filter subsystem.
+.Sh SYNOPSIS
+.Nm
+.Op Fl hilqsv
+.Fl u Ar unit
+.Op Fl a Ar offset
+.Op Fl d Ar offset value
+.Op Fl k Ar increment
+.Op Fl m Ar length
+.Op Fl p Ar offset
+.Op Fl r Ar random
+.Op Fl t Ar delay
+.Sh DESCRIPTION
+Use
+.Nm
+to exercise the content filter subsystem.
+.Pp
+The flags have the following meaning:
+.Bl -tag -width -indent
+.It Fl a Ar offset
+Auto start filtering with given offset.
+.It Fl a Ar offset value
+Default values for offset passin, peekin, passout, peekout, pass or peek.
+.It Fl h
+Display this help.
+.It Fl i
+Interactive mode.
+.It Fl k Ar increment
+Peek mode with increment.
+.It Fl l
+Pass loopback traffic.
+.It Fl m Ar length
+Maximum dump length.
+.It Fl p Ar offset
+Pass mode (all or after given offset if it is > 0).
+.It Fl q
+Decrease verbosity.
+.It Fl r Ar rate
+Random drop rate.
+.It Fl s
+display content filter statistics (all, sock, filt, cfil).
+.It Fl t Ar delay
+Pass delay in microseconds.
+.It Fl u Ar unit
+NECP filter control unit.
+.It Fl v
+Increase verbosity.
+.El
+.Pp
+.Sh SEE ALSO
+.Xr neutil 1 \" rdar://16115914
diff --git a/network_cmds/cfilutil/cfilutil.c b/network_cmds/cfilutil/cfilutil.c new file mode 100644 index 0000000..4aaa719 --- /dev/null +++ b/network_cmds/cfilutil/cfilutil.c @@ -0,0 +1,987 @@ +/* + * Copyright (c) 2013-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/event.h> +#include <sys/time.h> +#include <sys/sys_domain.h> +#include <sys/ioctl.h> +#include <sys/kern_control.h> +#include <sys/queue.h> +#include <net/content_filter.h> +#include <netinet/in.h> +#include <stdio.h> +#include <err.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <sysexits.h> + +extern void print_filter_list(void); +extern void print_socket_list(void); +extern void print_cfil_stats(void); + +#define MAX_BUFFER (65536 + 1024) + +#define MAXHEXDUMPCOL 16 + + +enum { + MODE_NONE = 0, + MODE_INTERACTIVE = 0x01, + MODE_PEEK = 0x02, + MODE_PASS = 0x04, + MODE_DELAY = 0x08 +}; +int mode = MODE_NONE; + +unsigned long delay_ms = 0; +struct timeval delay_tv = { 0, 0 }; +long verbosity = 0; +uint32_t necp_control_unit = 0; +unsigned long auto_start = 0; +uint64_t peek_inc = 0; +uint64_t pass_offset = 0; +struct timeval now, deadline; +int sf = -1; +int pass_loopback = 0; +uint32_t random_drop = 0; +uint32_t event_total = 0; +uint32_t event_dropped = 0; + +uint64_t default_in_pass = 0; +uint64_t default_in_peek = 0; +uint64_t default_out_pass = 0; +uint64_t default_out_peek = 0; + +unsigned long max_dump_len = 32; + +TAILQ_HEAD(sock_info_head, sock_info) sock_info_head = TAILQ_HEAD_INITIALIZER(sock_info_head); + + +struct sock_info { + TAILQ_ENTRY(sock_info) si_link; + cfil_sock_id_t si_sock_id; + struct timeval si_deadline; + uint64_t si_in_pass; + uint64_t si_in_peek; + uint64_t si_out_pass; + uint64_t si_out_peek; +}; + +static void +HexDump(void *data, size_t len) +{ + size_t i, j, k; + unsigned char *ptr = (unsigned char *)data; + unsigned char buf[32 + 3 * MAXHEXDUMPCOL + 2 + MAXHEXDUMPCOL + 1]; + + for (i = 0; i < len; i += MAXHEXDUMPCOL) { + k = snprintf((char *)buf, sizeof(buf), "\t0x%04lx: ", i); + for (j = i; j < i + MAXHEXDUMPCOL; j++) { + if (j < len) { + unsigned char msnbl = ptr[j] >> 4; + unsigned char lsnbl = ptr[j] & 0x0f; + + buf[k++] = msnbl < 10 ? msnbl + '0' : msnbl + 'a' - 10; + buf[k++] = lsnbl < 10 ? lsnbl + '0' : lsnbl + 'a' - 10; + } else { + buf[k++] = ' '; + buf[k++] = ' '; + } + if ((j % 2) == 1) + buf[k++] = ' '; + if ((j % MAXHEXDUMPCOL) == MAXHEXDUMPCOL - 1) + buf[k++] = ' '; + } + + buf[k++] = ' '; + buf[k++] = ' '; + + for (j = i; j < i + MAXHEXDUMPCOL && j < len; j++) { + if (isprint(ptr[j])) + buf[k++] = ptr[j]; + else + buf[k++] = '.'; + } + buf[k] = 0; + printf("%s\n", buf); + } +} + +void +print_hdr(struct cfil_msg_hdr *hdr) +{ + const char *typestr = "unknown"; + const char *opstr = "unknown"; + + if (hdr->cfm_type == CFM_TYPE_EVENT) { + typestr = "event"; + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: + opstr = "attached"; + break; + case CFM_OP_SOCKET_CLOSED: + opstr = "closed"; + break; + case CFM_OP_DATA_OUT: + opstr = "dataout"; + break; + case CFM_OP_DATA_IN: + opstr = "datain"; + break; + case CFM_OP_DISCONNECT_OUT: + opstr = "disconnectout"; + break; + case CFM_OP_DISCONNECT_IN: + opstr = "disconnectin"; + break; + + default: + break; + } + } else if (hdr->cfm_type == CFM_TYPE_ACTION) { + typestr = "action"; + switch (hdr->cfm_op) { + case CFM_OP_DATA_UPDATE: + opstr = "update"; + break; + case CFM_OP_DROP: + opstr = "drop"; + break; + + default: + break; + } + + } + printf("%s %s len %u version %u type %u op %u sock_id 0x%llx\n", + typestr, opstr, + hdr->cfm_len, hdr->cfm_version, hdr->cfm_type, + hdr->cfm_op, hdr->cfm_sock_id); +} + +void +print_data_req(struct cfil_msg_data_event *data_req) +{ + size_t datalen; + void *databuf; + + if (verbosity <= 0) + return; + + print_hdr(&data_req->cfd_msghdr); + + printf(" start %llu end %llu\n", + data_req->cfd_start_offset, data_req->cfd_end_offset); + + datalen = (size_t)(data_req->cfd_end_offset - data_req->cfd_start_offset); + + databuf = (void *)(data_req + 1); + + if (verbosity > 1) + HexDump(databuf, MIN(datalen, max_dump_len)); +} + +void +print_action_msg(struct cfil_msg_action *action) +{ + if (verbosity <= 0) + return; + + print_hdr(&action->cfa_msghdr); + + if (action->cfa_msghdr.cfm_op == CFM_OP_DATA_UPDATE) + printf(" out pass %llu peek %llu in pass %llu peek %llu\n", + action->cfa_out_pass_offset, action->cfa_out_peek_offset, + action->cfa_in_pass_offset, action->cfa_in_peek_offset); +} + +struct sock_info * +find_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info; + + TAILQ_FOREACH(sock_info, &sock_info_head, si_link) { + if (sock_info->si_sock_id == sockid) + return (sock_info); + } + return (NULL); +} + +struct sock_info * +add_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info; + + if (find_sock_info(sockid) != NULL) + return (NULL); + + sock_info = calloc(1, sizeof(struct sock_info)); + if (sock_info == NULL) + err(EX_OSERR, "calloc()"); + sock_info->si_sock_id = sockid; + TAILQ_INSERT_TAIL(&sock_info_head, sock_info, si_link); + + return (sock_info); +} + +void +remove_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info = find_sock_info(sockid); + + if (sock_info != NULL) { + TAILQ_REMOVE(&sock_info_head, sock_info, si_link); + free(sock_info); + } +} + +/* return 0 if timer is already set */ +int +set_sock_info_deadline(struct sock_info *sock_info) +{ + if (timerisset(&sock_info->si_deadline)) + return (0); + + timeradd(&now, &sock_info->si_deadline, &sock_info->si_deadline); + + if (!timerisset(&deadline)) { + timeradd(&now, &delay_tv, &deadline); + } + + return (1); +} + +void +send_action_message(uint32_t op, struct sock_info *sock_info, int nodelay) +{ + struct cfil_msg_action action; + + if (!nodelay && delay_ms) { + set_sock_info_deadline(sock_info); + return; + } + bzero(&action, sizeof(struct cfil_msg_action)); + action.cfa_msghdr.cfm_len = sizeof(struct cfil_msg_action); + action.cfa_msghdr.cfm_version = CFM_VERSION_CURRENT; + action.cfa_msghdr.cfm_type = CFM_TYPE_ACTION; + action.cfa_msghdr.cfm_op = op; + action.cfa_msghdr.cfm_sock_id = sock_info->si_sock_id; + switch (op) { + case CFM_OP_DATA_UPDATE: + action.cfa_out_pass_offset = sock_info->si_out_pass; + action.cfa_out_peek_offset = sock_info->si_out_peek; + action.cfa_in_pass_offset = sock_info->si_in_pass; + action.cfa_in_peek_offset = sock_info->si_in_peek; + break; + + default: + break; + } + + if (verbosity > -1) + print_action_msg(&action); + + if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1) + warn("send()"); + + timerclear(&sock_info->si_deadline); +} + +void +process_delayed_actions() +{ + struct sock_info *sock_info; + + TAILQ_FOREACH(sock_info, &sock_info_head, si_link) { + if (timerisset(&sock_info->si_deadline) && + timercmp(&sock_info->si_deadline, &now, >=)) + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 1); + } +} + +int +set_non_blocking(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + warn("fcntl(F_GETFL)"); + return (-1); + } + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + warn("fcntl(F_SETFL)"); + return (-1); + } + return (0); +} + +int +offset_from_str(const char *str, uint64_t *ret_val) +{ + char *endptr; + uint64_t offset; + int success = 1; + + if (strcasecmp(str, "max") == 0 || strcasecmp(str, "all") == 0) + offset = CFM_MAX_OFFSET; + else { + offset = strtoull(str, &endptr, 0); + if (*str == '\0' || *endptr != '\0') + success = 0; + } + if (success) + *ret_val = offset; + return (success); +} + +#define IN6_IS_ADDR_V4MAPPED_LOOPBACK(a) \ + ((*(const __uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(INADDR_LOOPBACK))) + + +int +is_loopback(struct cfil_msg_data_event *data_req) +{ + if (data_req->cfc_dst.sa.sa_family == AF_INET && + ntohl(data_req->cfc_dst.sin.sin_addr.s_addr) == INADDR_LOOPBACK) + return (1); + if (data_req->cfc_dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr)) + return (1); + if (data_req->cfc_dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr)) + return (1); + + if (data_req->cfc_src.sa.sa_family == AF_INET && + ntohl(data_req->cfc_src.sin.sin_addr.s_addr) == INADDR_LOOPBACK) + return (1); + if (data_req->cfc_src.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr)) + return (1); + if (data_req->cfc_src.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr)) + return (1); + + return (0); +} + +int +drop(struct sock_info *sock_info) +{ + event_total++; + if (random_drop > 0) { + uint32_t r = arc4random(); + if (r <= random_drop) { + event_dropped++; + printf("dropping 0x%llx dropped %u total %u rate %f\n", + sock_info->si_sock_id, + event_dropped, event_total, + (double)event_dropped/(double)event_total * 100); + send_action_message(CFM_OP_DROP, sock_info, 0); + return (1); + } + } + return (0); +} + +int +doit() +{ + struct sockaddr_ctl sac; + struct ctl_info ctl_info; + void *buffer = NULL; + struct cfil_msg_hdr *hdr; + int kq = -1; + struct kevent kv; + int fdin = fileno(stdin); + char *linep = NULL; + size_t linecap = 0; + char *cmdptr = NULL; + char *argptr = NULL; + size_t cmdlen = 0; + struct cfil_msg_action action; + cfil_sock_id_t last_sock_id = 0; + struct sock_info *sock_info = NULL; + struct timeval last_time, elapsed, delta; + struct timespec interval, *timeout = NULL; + + kq = kqueue(); + if (kq == -1) + err(1, "kqueue()"); + + sf = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (sf == -1) + err(1, "socket()"); + + bzero(&ctl_info, sizeof(struct ctl_info)); + strlcpy(ctl_info.ctl_name, CONTENT_FILTER_CONTROL_NAME, sizeof(ctl_info.ctl_name)); + if (ioctl(sf, CTLIOCGINFO, &ctl_info) == -1) + err(1, "ioctl(CTLIOCGINFO)"); + + if (fcntl(sf, F_SETNOSIGPIPE, 1) == -1) + err(1, "fcntl(F_SETNOSIGPIPE)"); + + bzero(&sac, sizeof(struct sockaddr_ctl)); + sac.sc_len = sizeof(struct sockaddr_ctl); + sac.sc_family = AF_SYSTEM; + sac.ss_sysaddr = AF_SYS_CONTROL; + sac.sc_id = ctl_info.ctl_id; + + if (connect(sf, (struct sockaddr *)&sac, sizeof(struct sockaddr_ctl)) == -1) + err(1, "connect()"); + + if (set_non_blocking(sf) == -1) + err(1, "set_non_blocking(sf)"); + + if (setsockopt(sf, SYSPROTO_CONTROL, CFIL_OPT_NECP_CONTROL_UNIT, + &necp_control_unit, sizeof(uint32_t)) == -1) + err(1, "setsockopt(CFIL_OPT_NECP_CONTROL_UNIT, %u)", necp_control_unit); + + bzero(&kv, sizeof(struct kevent)); + kv.ident = sf; + kv.filter = EVFILT_READ; + kv.flags = EV_ADD; + if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1) + err(1, "kevent(sf %d)", sf); + + /* + * We can only read from an interactive terminal + */ + if (isatty(fdin)) { + bzero(&kv, sizeof(struct kevent)); + kv.ident = fdin; + kv.filter = EVFILT_READ; + kv.flags = EV_ADD; + if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1) + err(1, "kevent(fdin %d)", fdin); + } + + buffer = malloc(MAX_BUFFER); + if (buffer == NULL) + err(1, "malloc()"); + + gettimeofday(&now, NULL); + + while (1) { + last_time = now; + if (delay_ms && timerisset(&deadline)) { + timersub(&deadline, &now, &delta); + TIMEVAL_TO_TIMESPEC(&delta, &interval); + timeout = &interval; + } else { + timeout = NULL; + } + + if (kevent(kq, NULL, 0, &kv, 1, timeout) == -1) { + if (errno == EINTR) + continue; + err(1, "kevent()"); + } + gettimeofday(&now, NULL); + timersub(&now, &last_time, &elapsed); + if (delay_ms && timerisset(&deadline)) { + if (timercmp(&now, &deadline, >=)) { + process_delayed_actions(); + interval.tv_sec = 0; + interval.tv_nsec = 0; + } + } + + if (kv.ident == sf && kv.filter == EVFILT_READ) { + while (1) { + ssize_t nread; + + nread = recv(sf, buffer, MAX_BUFFER, 0); + if (nread == 0) { + warnx("recv(sf) returned 0, connection closed"); + break; + } + if (nread == -1) { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK) + break; + err(1, "recv()"); + + } + if (nread < sizeof(struct cfil_msg_hdr)) + errx(1, "too small"); + hdr = (struct cfil_msg_hdr *)buffer; + + + if (hdr->cfm_type != CFM_TYPE_EVENT) { + warnx("not a content filter event type %u", hdr->cfm_type); + continue; + } + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: { + struct cfil_msg_sock_attached *msg_attached = (struct cfil_msg_sock_attached *)hdr; + + if (verbosity > -2) + print_hdr(hdr); + if (verbosity > -1) + printf(" fam %d type %d proto %d pid %u epid %u\n", + msg_attached->cfs_sock_family, + msg_attached->cfs_sock_type, + msg_attached->cfs_sock_protocol, + msg_attached->cfs_pid, + msg_attached->cfs_e_pid); + break; + } + case CFM_OP_SOCKET_CLOSED: + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: + if (verbosity > -2) + print_hdr(hdr); + break; + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: + if (verbosity > -3) + print_data_req((struct cfil_msg_data_event *)hdr); + break; + default: + warnx("unknown content filter event op %u", hdr->cfm_op); + continue; + } + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: + sock_info = add_sock_info(hdr->cfm_sock_id); + if (sock_info == NULL) { + warnx("sock_id %llx already exists", hdr->cfm_sock_id); + continue; + } + break; + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: + case CFM_OP_SOCKET_CLOSED: + sock_info = find_sock_info(hdr->cfm_sock_id); + + if (sock_info == NULL) { + warnx("unexpected data message, sock_info is NULL"); + continue; + } + break; + default: + warnx("unknown content filter event op %u", hdr->cfm_op); + continue; + } + + + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: { + if ((mode & MODE_PASS) || (mode & MODE_PEEK) || auto_start) { + sock_info->si_out_pass = default_out_pass; + sock_info->si_out_peek = (mode & MODE_PEEK) ? peek_inc : (mode & MODE_PASS) ? CFM_MAX_OFFSET : default_out_peek; + sock_info->si_in_pass = default_in_pass; + sock_info->si_in_peek = (mode & MODE_PEEK) ? peek_inc : (mode & MODE_PASS) ? CFM_MAX_OFFSET : default_in_peek; + + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + } + break; + } + case CFM_OP_SOCKET_CLOSED: { + remove_sock_info(hdr->cfm_sock_id); + sock_info = NULL; + break; + } + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: { + struct cfil_msg_data_event *data_req = (struct cfil_msg_data_event *)hdr; + + if (pass_loopback && is_loopback(data_req)) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } else { + if (drop(sock_info)) + continue; + + if ((mode & MODE_PASS)) { + if (data_req->cfd_msghdr.cfm_op == CFM_OP_DATA_OUT) { + if (pass_offset == 0 || pass_offset == CFM_MAX_OFFSET) + sock_info->si_out_pass = data_req->cfd_end_offset; + else if (data_req->cfd_end_offset > pass_offset) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } + sock_info->si_out_peek = (mode & MODE_PEEK) ? + data_req->cfd_end_offset + peek_inc : 0; + } else { + if (pass_offset == 0 || pass_offset == CFM_MAX_OFFSET) + sock_info->si_in_pass = data_req->cfd_end_offset; + else if (data_req->cfd_end_offset > pass_offset) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } + sock_info->si_in_peek = (mode & MODE_PEEK) ? + data_req->cfd_end_offset + peek_inc : 0; + } + } else { + break; + } + } + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + + break; + } + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: { + if (drop(sock_info)) + continue; + + if ((mode & MODE_PASS)) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + } + break; + } + default: + warnx("unkown message op %u", hdr->cfm_op); + break; + } + if (sock_info) + last_sock_id = sock_info->si_sock_id; + } + } + if (kv.ident == fdin && kv.filter == EVFILT_READ) { + ssize_t nread; + uint64_t offset = 0; + int nitems; + int op = 0; + + nread = getline(&linep, &linecap, stdin); + if (nread == -1) + errx(1, "getline()"); + + if (verbosity > 2) + printf("linecap %lu nread %lu\n", linecap, nread); + if (nread > 0) + linep[nread - 1] = '\0'; + + if (verbosity > 2) + HexDump(linep, linecap); + + if (*linep == 0) + continue; + + if (cmdptr == NULL || argptr == NULL || linecap > cmdlen) { + cmdlen = linecap; + cmdptr = realloc(cmdptr, cmdlen); + argptr = realloc(argptr, cmdlen); + } + + /* + * Trick to support unisgned and hexadecimal arguments + * as I can't figure out sscanf() conversions + */ + nitems = sscanf(linep, "%s %s", cmdptr, argptr); + if (nitems == 0) { + warnx("I didn't get that..."); + continue; + } else if (nitems > 1) { + if (offset_from_str(argptr, &offset) == 0) { + warnx("I didn't get that either..."); + continue; + } + } + if (verbosity > 2) + printf("nitems %d %s %s\n", nitems, cmdptr, argptr); + + bzero(&action, sizeof(struct cfil_msg_action)); + action.cfa_msghdr.cfm_len = sizeof(struct cfil_msg_action); + action.cfa_msghdr.cfm_version = CFM_VERSION_CURRENT; + action.cfa_msghdr.cfm_type = CFM_TYPE_ACTION; + + if (strcasecmp(cmdptr, "passout") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = offset; + } else if (strcasecmp(cmdptr, "passin") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_in_pass_offset = offset; + } else if (strcasecmp(cmdptr, "pass") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = offset; + action.cfa_in_pass_offset = offset; + } else if (strcasecmp(cmdptr, "peekout") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = offset; + } else if (strcasecmp(cmdptr, "peekin") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_in_peek_offset = offset; + } else if (strcasecmp(cmdptr, "peek") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = offset; + action.cfa_in_peek_offset = offset; + } else if (strcasecmp(cmdptr, "start") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = 0; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_pass_offset = 0; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "peekall") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "passall") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = CFM_MAX_OFFSET; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_pass_offset = CFM_MAX_OFFSET; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "drop") == 0) + op = CFM_OP_DROP; + else if (strcasecmp(cmdptr, "sock") == 0) { + last_sock_id = offset; + printf("last_sock_id 0x%llx\n", last_sock_id); + } else + warnx("syntax error"); + + if (op == CFM_OP_DATA_UPDATE || op == CFM_OP_DROP) { + action.cfa_msghdr.cfm_op = op; + action.cfa_msghdr.cfm_sock_id = last_sock_id; + print_action_msg(&action); + + if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1) + warn("send()"); + } + } + } + + return 0; +} + +static const char * +basename(const char * str) +{ + const char *last_slash = strrchr(str, '/'); + + if (last_slash == NULL) + return (str); + else + return (last_slash + 1); +} + +struct option_desc { + const char *option; + const char *description; + int required; +}; + +struct option_desc option_desc_list[] = { + { "-a offset", "auto start with offset", 0 }, + { "-d offset value", "default offset value for passin, peekin, passout, peekout, pass, peek", 0 }, + { "-h", "dsiplay this help", 0 }, + { "-i", "interactive mode", 0 }, + { "-k increment", "peek mode with increment", 0 }, + {"-l", "pass loopback", 0 }, + { "-m length", "max dump length", 0 }, + { "-p offset", "pass mode (all or after given offset if > 0)", 0 }, + { "-q", "decrease verbose level", 0 }, + { "-r random", "random drop rate", 0 }, + { "-s ", "display content filter statistics (all, sock, filt, cfil)", 0 }, + { "-t delay", "pass delay in microseconds", 0 }, + { "-u unit", "NECP filter control unit", 1 }, + { "-v", "increase verbose level", 0 }, + { NULL, NULL, 0 } /* Mark end of list */ +}; + +static void +usage(const char *cmd) +{ + struct option_desc *option_desc; + char *usage_str = malloc(LINE_MAX); + size_t usage_len; + + if (usage_str == NULL) + err(1, "%s: malloc(%d)", __func__, LINE_MAX); + + usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd)); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + int len; + + if (option_desc->required) + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option); + else + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option); + if (len < 0) + err(1, "%s: snprintf(", __func__); + + usage_len += len; + if (usage_len > LINE_MAX) + break; + } + printf("%s\n", usage_str); + printf("options:\n"); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + printf(" %-20s # %s\n", option_desc->option, option_desc->description); + } + +} + +int +main(int argc, char * const argv[]) +{ + int ch; + double d; + int stats_sock_list = 0; + int stats_filt_list = 0; + int stats_cfil_stats = 0; + + while ((ch = getopt(argc, argv, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) { + switch (ch) { + case 'a': + auto_start = strtoul(optarg, NULL, 0); + break; + case 'd': { + if (optind >= argc) + errx(1, "'-d' needs 2 parameters"); + if (strcasecmp(optarg, "passout") == 0) { + if (offset_from_str(argv[optind], &default_out_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "passin") == 0) { + if (offset_from_str(argv[optind], &default_in_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "pass") == 0) { + if (offset_from_str(argv[optind], &default_out_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + default_in_pass = default_out_pass; + } else if (strcasecmp(optarg, "peekout") == 0) { + if (offset_from_str(argv[optind], &default_out_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "peekin") == 0) { + if (offset_from_str(argv[optind], &default_in_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "peek") == 0) { + if (offset_from_str(argv[optind], &default_out_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + default_in_peek = default_out_peek; + } else + errx(1, "syntax error"); + break; + } + case 'h': + usage(argv[0]); + exit(0); + case 'i': + mode |= MODE_INTERACTIVE; + break; + case 'k': + mode |= MODE_PEEK; + if (offset_from_str(optarg, &peek_inc) == 0) + errx(1, "bad peek offset: %s", optarg); + break; + case 'l': + pass_loopback = 1; + break; + case 'm': + max_dump_len = strtoul(optarg, NULL, 0); + break; + case 'p': + mode |= MODE_PASS; + if (offset_from_str(optarg, &pass_offset) == 0) + errx(1, "bad pass offset: %s", optarg); + break; + case 'q': + verbosity--; + break; + case 'r': + d = strtod(optarg, NULL); + if (d < 0 || d > 1) + errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg); + random_drop = (uint32_t)(d * UINT32_MAX); + break; + case 's': + if (strcasecmp(optarg, "all") == 0) { + stats_sock_list = 1; + stats_filt_list = 1; + stats_cfil_stats = 1; + } else if (strcasecmp(optarg, "sock") == 0) { + stats_sock_list = 1; + } else if (strcasecmp(optarg, "filt") == 0) { + stats_filt_list = 1; + } else if (strcasecmp(optarg, "cfil") == 0) { + stats_cfil_stats = 1; + } else { + warnx("# Error: unknown type of statistic: %s", optarg); + usage(argv[0]); + exit(0); + } + break; + case 't': + mode |= MODE_DELAY; + delay_ms = strtoul(optarg, NULL, 0); + delay_tv.tv_sec = delay_ms / 1000; + delay_tv.tv_usec = (delay_ms % 1000) * 1000; + break; + case 'u': + necp_control_unit = (uint32_t)strtoul(optarg, NULL, 0); + break; + case 'v': + verbosity++; + break; + default: + errx(1, "# syntax error, unknow option '%d'", ch); + usage(argv[0]); + exit(0); + } + } + + if (stats_filt_list) + print_filter_list(); + if (stats_sock_list) + print_socket_list(); + if (stats_cfil_stats) + print_cfil_stats(); + if (necp_control_unit == 0 && (stats_filt_list || stats_sock_list || stats_cfil_stats)) + return (0); + + if (necp_control_unit == 0) { + warnx("necp filter control unit is 0"); + usage(argv[0]); + exit(EX_USAGE); + } + doit(); + + + return (0); +} + diff --git a/network_cmds/dnctl/dnctl.8 b/network_cmds/dnctl/dnctl.8 new file mode 100644 index 0000000..4da4724 --- /dev/null +++ b/network_cmds/dnctl/dnctl.8 @@ -0,0 +1,508 @@ +.Dd August 13, 2002 +.Dt DNCTL 8 +.Os Darwin +.Sh NAME +.Nm dnctl +.Nd Traffic shaper control program +.Sh SYNOPSIS +.Nm +.Op Fl anqs +.Brq Cm list | show +.Nm +.Op Fl f | q +.Cm flush +.Nm +.Op Fl q +.Brq Cm delete +.Op Ar number ... +.Nm +.Brq Cm pipe | queue +.Ar number +.Cm config +.Ar config-options +.Nm +.Op Fl s Op Ar field +.Brq Cm pipe | queue +.Brq Cm delete | list | show +.Op Ar number ... +.Nm +.Op Fl nq +.Oo +.Fl p Ar preproc +.Oo +.Ar preproc-flags +.Oc +.Oc +.Ar pathname +.Sh DESCRIPTION +.Pp +The +.Nm +utility is the user interface for controlling the +.Xr dummynet 4 +traffic shaper. +.Pp +.Nm dummynet +operates by first using a packet filter to classify packets and divide them into +.Em flows , +using any match pattern that can be used in +.Nm +rules. +Depending on local policies, a flow can contain packets for a single +TCP connection, or from/to a given host, or entire subnet, or a +protocol type, etc. +.Pp +Packets belonging to the same flow are then passed to either of two +different objects, which implement the traffic regulation: +.Bl -hang -offset XXXX +.It Em pipe +A pipe emulates a link with given bandwidth, propagation delay, +queue size and packet loss rate. +Packets are queued in front of the pipe as they come out from the classifier, +and then transferred to the pipe according to the pipe's parameters. +.Pp +.It Em queue +A queue +is an abstraction used to implement the WF2Q+ +(Worst-case Fair Weighted Fair Queueing) policy, which is +an efficient variant of the WFQ policy. +.br +The queue associates a +.Em weight +and a reference pipe to each flow, and then all backlogged (i.e., +with packets queued) flows linked to the same pipe share the pipe's +bandwidth proportionally to their weights. +Note that weights are not priorities; a flow with a lower weight +is still guaranteed to get its fraction of the bandwidth even if a +flow with a higher weight is permanently backlogged. +.Pp +.El +In practice, +.Em pipes +can be used to set hard limits to the bandwidth that a flow can use, whereas +.Em queues +can be used to determine how different flow share the available bandwidth. +.Pp +The +.Em pipe +and +.Em queue +configuration commands are the following: +.Bd -ragged -offset indent +.Cm pipe Ar number Cm config Ar pipe-configuration +.Pp +.Cm queue Ar number Cm config Ar queue-configuration +.Ed +.Pp +The following parameters can be configured for a pipe: +.Pp +.Bl -tag -width indent -compact +.It Cm bw Ar bandwidth | device +Bandwidth, measured in +.Sm off +.Op Cm K | M +.Brq Cm bit/s | Byte/s . +.Sm on +.Pp +A value of 0 (default) means unlimited bandwidth. +The unit must immediately follow the number, as in +.Pp +.Dl "dnctl pipe 1 config bw 300Kbit/s" +.Pp +If a device name is specified instead of a numeric value, as in +.Pp +.Dl "dnctl pipe 1 config bw tun0" +.Pp +then the transmit clock is supplied by the specified device. +At the moment no +device supports this +functionality. +.Pp +.It Cm delay Ar ms-delay +Propagation delay, measured in milliseconds. +The value is rounded to the next multiple of the clock tick +(typically 10ms, but it is a good practice to run kernels +with +.Dq "options HZ=1000" +to reduce +the granularity to 1ms or less). +Default value is 0, meaning no delay. +.El +.Pp +The following parameters can be configured for a queue: +.Pp +.Bl -tag -width indent -compact +.It Cm pipe Ar pipe_nr +Connects a queue to the specified pipe. +Multiple queues (with the same or different weights) can be connected to +the same pipe, which specifies the aggregate rate for the set of queues. +.Pp +.It Cm weight Ar weight +Specifies the weight to be used for flows matching this queue. +The weight must be in the range 1..100, and defaults to 1. +.El +.Pp +Finally, the following parameters can be configured for both +pipes and queues: +.Pp +.Bl -tag -width XXXX -compact +.It Cm buckets Ar hash-table-size +Specifies the size of the hash table used for storing the +various queues. +Default value is 64 controlled by the +.Xr sysctl 8 +variable +.Em net.inet.ip.dummynet.hash_size , +allowed range is 16 to 65536. +.Pp +.It Cm mask Ar mask-specifier +Packets sent to a given pipe or queue by an +.Nm +rule can be further classified into multiple flows, each of which is then +sent to a different +.Em dynamic +pipe or queue. +A flow identifier is constructed by masking the IP addresses, +ports and protocol types as specified with the +.Cm mask +options in the configuration of the pipe or queue. +For each different flow identifier, a new pipe or queue is created +with the same parameters as the original object, and matching packets +are sent to it. +.Pp +Thus, when +.Em dynamic pipes +are used, each flow will get the same bandwidth as defined by the pipe, +whereas when +.Em dynamic queues +are used, each flow will share the parent's pipe bandwidth evenly +with other flows generated by the same queue (note that other queues +with different weights might be connected to the same pipe). +.br +Available mask specifiers are a combination of one or more of the following: +.Pp +.Cm dst-ip Ar mask , +.Cm dst-ip6 Ar mask , +.Cm src-ip Ar mask , +.Cm src-ip6 Ar mask , +.Cm dst-port Ar mask , +.Cm src-port Ar mask , +.Cm proto Ar mask +or +.Cm all , +.Pp +where the latter means all bits in all fields are significant. +.Pp +.It Cm noerror +When a packet is dropped by a dummynet queue or pipe, the error +is normally reported to the caller routine in the kernel, in the +same way as it happens when a device queue fills up. Setting this +option reports the packet as successfully delivered, which can be +needed for some experimental setups where you want to simulate +loss or congestion at a remote router. +.Pp +.It Cm plr Ar packet-loss-rate +Packet loss rate. +Argument +.Ar packet-loss-rate +is a floating-point number between 0 and 1, with 0 meaning no +loss, 1 meaning 100% loss. +The loss rate is internally represented on 31 bits. +.Pp +.It Cm queue Brq Ar slots | size Ns Cm Kbytes +Queue size, in +.Ar slots +or +.Cm KBytes . +Default value is 50 slots, which +is the typical queue size for Ethernet devices. +Note that for slow speed links you should keep the queue +size short or your traffic might be affected by a significant +queueing delay. +E.g., 50 max-sized ethernet packets (1500 bytes) mean 600Kbit +or 20s of queue on a 30Kbit/s pipe. +Even worse effect can result if you get packets from an +interface with a much larger MTU, e.g. the loopback interface +with its 16KB packets. +.Pp +.It Cm red | gred Ar w_q Ns / Ns Ar min_th Ns / Ns Ar max_th Ns / Ns Ar max_p +Make use of the RED (Random Early Detection) queue management algorithm. +.Ar w_q +and +.Ar max_p +are floating +point numbers between 0 and 1 (0 not included), while +.Ar min_th +and +.Ar max_th +are integer numbers specifying thresholds for queue management +(thresholds are computed in bytes if the queue has been defined +in bytes, in slots otherwise). +The +.Xr dummynet 4 +also supports the gentle RED variant (gred). +.Pp +Three +.Xr sysctl 8 +variables can be used to control the RED behaviour: +.Bl -tag -width indent +.It Em net.inet.ip.dummynet.red_lookup_depth +specifies the accuracy in computing the average queue +when the link is idle (defaults to 256, must be greater than zero) +.It Em net.inet.ip.dummynet.red_avg_pkt_size +specifies the expected average packet size (defaults to 512, must be +greater than zero) +.It Em net.inet.ip.dummynet.red_max_pkt_size +specifies the expected maximum packet size, only used when queue +thresholds are in bytes (defaults to 1500, must be greater than zero). +.El +.El +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +While listing, show counter values. +The +.Cm show +command just implies this option. +.It Fl f +Don't ask for confirmation for commands that can cause problems +if misused, +.No i.e. Cm flush . +If there is no tty associated with the process, this is implied. +.It Fl h +Displays a short help. +.It Fl n +Only check syntax of the command strings, without actually passing +them to the kernel. +.It Fl q +While +.Cm add Ns ing , +.Cm zero Ns ing , +.Cm resetlog Ns ging +or +.Cm flush Ns ing , +be quiet about actions +(implies +.Fl f ) . +This is useful for adjusting rules by executing multiple +.Nm +commands in a script +or by processing a file of many +.Nm +rules across a remote login session. +If a +.Cm flush +is performed in normal (verbose) mode (with the default kernel +configuration), it prints a message. +Because all rules are flushed, the message might not be delivered +to the login session, causing the remote login session to be closed +and the remainder of the ruleset to not be processed. +Access to the console would then be required to recover. +.It Fl s Op Ar field +While listing pipes, sort according to one of the four +counters (total or current packets or bytes). +.It Fl v +Be verbose. +.El +.Pp +To ease configuration, rules can be put into a file which is +processed using +.Nm +as shown in the last synopsis line. +An absolute +.Ar pathname +must be used. +The file will be read line by line and applied as arguments to the +.Nm +utility. +.Pp +Optionally, a preprocessor can be specified using +.Fl p Ar preproc +where +.Ar pathname +is to be piped through. +Useful preprocessors include +.Xr cpp 1 +and +.Xr m4 1 . +If +.Ar preproc +doesn't start with a slash +.Pq Ql / +as its first character, the usual +.Ev PATH +name search is performed. +Care should be taken with this in environments where not all +file systems are mounted (yet) by the time +.Nm +is being run (e.g. when they are mounted over NFS). +Once +.Fl p +has been specified, any additional arguments as passed on to the preprocessor +for interpretation. +This allows for flexible configuration files (like conditionalizing +them on the local hostname) and the use of macros to centralize +frequently required arguments like IP addresses. +.El +.Sh CHECKLIST +Here are some important points to consider when designing your +rules: +.Bl -bullet +.It +Remember that you filter both packets going +.Cm in +and +.Cm out . +Most connections need packets going in both directions. +.It +Remember to test very carefully. +It is a good idea to be near the console when doing this. +.It +Don't forget the loopback interface. +.El +.Sh SYSCTL VARIABLES +A set of +.Xr sysctl 8 +variables controls the behaviour of the +.Nm dummynet +module. +These are shown below together with their default value +(but always check with the +.Xr sysctl 8 +command what value is actually in use) and meaning: +.Bl -tag -width indent +.It Em net.inet.ip.dummynet.expire : No 1 +Lazily delete dynamic pipes/queue once they have no pending traffic. +You can disable this by setting the variable to 0, in which case +the pipes/queues will only be deleted when the threshold is reached. +.It Em net.inet.ip.dummynet.hash_size : No 64 +Default size of the hash table used for dynamic pipes/queues. +This value is used when no +.Cm buckets +option is specified when configuring a pipe/queue. +.It Em net.inet.ip.dummynet.max_chain_len : No 16 +Target value for the maximum number of pipes/queues in a hash bucket. +The product +.Cm max_chain_len*hash_size +is used to determine the threshold over which empty pipes/queues +will be expired even when +.Cm net.inet.ip.dummynet.expire=0 . +.It Em net.inet.ip.dummynet.red_lookup_depth : No 256 +.It Em net.inet.ip.dummynet.red_avg_pkt_size : No 512 +.It Em net.inet.ip.dummynet.red_max_pkt_size : No 1500 +Parameters used in the computations of the drop probability +for the RED algorithm. +.El +.Sh EXAMPLES +The following rules show some of the applications of +for simulations and the like by using +.Em dummynet +rules in +.Xr pf.conf 8 +configuration files. +.Pp +To drop random incoming IPv4 and IPv6 ICMP packets with a probability of 5%, +create a pipe: +.Dl "dnctl pipe 10 config plr 0.05" +.Pp +and add these rules in your pf.conf file: +.Dl "dummynet in inet proto icmp all pipe 10" +.Dl "dummynet in inet6 proto ipv6-icmp all pipe 10" +.Pp +Should we want to simulate a bidirectional link with bandwidth +limitations, the correct way is to create a pipe for each direction: +.Dl "dnctl pipe 1 config bw 14Kbit/s queue 10Kbytes" +.Dl "dnctl pipe 2 config bw 1Kbit/s queue 10Kbytes" +.Pp +and add these rules in your pf.conf file: +.Dl "dummynet in all pipe 1" +.Dl "dummynet out all pipe 2" +.Pp +The above can be very useful, e.g. if you want to see how +your fancy Web page will look for a residential user who +is connected only through a slow link. +You should not use only one pipe for both directions, unless +you want to simulate a half-duplex medium (e.g. AppleTalk, +Ethernet, IRDA). +.Pp +Note that with the above rules the pipes receive traffic for both the +IPv4 and IPv6 protocols. +.Pp +Should we want to verify network performance with the RED queue +management algorithm, create this pipe: +.Dl "dnctl pipe 1 config bw 500Kbit/s queue 100 red 0.002/30/80/0.1" +.Pp +and then add these rules to you pf.conf file: +.Dl "dummynet all pipe 1" +.Pp +Another typical application of the traffic shaper is to +introduce some delay in the communication. +This can significantly affect applications which do a lot of Remote +Procedure Calls, and where the round-trip-time of the +connection often becomes a limiting factor much more than +bandwidth: +.Dl "dnctl pipe 1 config delay 250ms bw 1Mbit/s" +.Dl "dnctl pipe 2 config delay 250ms bw 1Mbit/s" +.Pp +and add these rules in your pf.conf file: +.Dl "dummynet in all pipe 1" +.Dl "dummynet out all pipe 2" +.Pp +Per-flow queueing can be useful for a variety of purposes. +A very simple one is counting traffic: +.Dl "dnctl pipe 1 config mask all" +.Pp +and add these statements in your pf.conf file: +.Dl "dummynet in quick proto tcp all pipe 1" +.Dl "dummynet out quick proto tcp all pipe 1" +.Dl "dummynet in quick proto udp all pipe 1" +.Dl "dummynet out quick proto udp all pipe 1" +.Dl "dummynet in quick all pipe 1" +.Dl "dummynet out quick all pipe 1" +.Pp +The above set of rules will create queues (and collect +statistics) for all traffic. +Because the pipes have no limitations, the only effect is +collecting statistics. +Note that we need six rules, not just the last two one, because +when +.Nm +tries to match IP packets it will not consider ports, so we +would not see connections on separate ports as different +ones. +.Sh SEE ALSO +.Xr cpp 1 , +.Xr dummynet 4 , +.Xr m4 1 , +.Xr ip 4 , +.Xr pfctl 8 , +.Xr pf.conf 5 , +.Xr protocols 5 , +.Xr services 5 , +.Xr sysctl 8 +.Sh AUTHORS +.An Ugen J. S. Antsilevich , +.An Poul-Henning Kamp , +.An Alex Nash , +.An Archie Cobbs , +.An Luigi Rizzo . +.Pp +.An -nosplit +API based upon code written by +.An Daniel Boulet +for BSDI. +.Pp +Work on +.Xr dummynet 4 +traffic shaper supported by Akamba Corp. +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 2.0 . +.Xr dummynet 4 +was introduced in +.Fx 2.2.8 . +Stateful extensions were introduced in +.Fx 4.0 . diff --git a/network_cmds/dnctl/dnctl.c b/network_cmds/dnctl/dnctl.c new file mode 100644 index 0000000..38cffe8 --- /dev/null +++ b/network_cmds/dnctl/dnctl.c @@ -0,0 +1,1313 @@ +/* + * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 2002-2003 Luigi Rizzo + * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp + * Copyright (c) 1994 Ugen J.S.Antsilevich + * + * Idea and grammar partially left from: + * Copyright (c) 1993 Daniel Boulet + * + * Redistribution and use in source forms, with and without modification, + * are permitted provided that this entire comment appears intact. + * + * Redistribution in binary form may occur without any restrictions. + * Obviously, it would be nice if you gave credit where credit is due + * but requiring it would be too onerous. + * + * This software is provided ``AS IS'' without any warranties of any kind. + */ + +/* + * Ripped off ipfw2.c + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <sysexits.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_dummynet.h> +#include <arpa/inet.h> + +/* + * Limit delay to avoid computation overflow + */ +#define MAX_DELAY (INT_MAX / 1000) + + +int +do_quiet, /* Be quiet in add and flush */ +do_pipe, /* this cmd refers to a pipe */ +do_sort, /* field to sort results (0 = no) */ +test_only, /* only check syntax */ +verbose; + +#define IP_MASK_ALL 0xffffffff + +/* + * _s_x is a structure that stores a string <-> token pairs, used in + * various places in the parser. Entries are stored in arrays, + * with an entry with s=NULL as terminator. + * The search routines are match_token() and match_value(). + * Often, an element with x=0 contains an error string. + * + */ +struct _s_x { + char const *s; + int x; +}; + +enum tokens { + TOK_NULL=0, + + TOK_ACCEPT, + TOK_COUNT, + TOK_PIPE, + TOK_QUEUE, + + TOK_PLR, + TOK_NOERROR, + TOK_BUCKETS, + TOK_DSTIP, + TOK_SRCIP, + TOK_DSTPORT, + TOK_SRCPORT, + TOK_ALL, + TOK_MASK, + TOK_BW, + TOK_DELAY, + TOK_RED, + TOK_GRED, + TOK_DROPTAIL, + TOK_PROTO, + TOK_WEIGHT, + + TOK_DSTIP6, + TOK_SRCIP6, +}; + +struct _s_x dummynet_params[] = { + { "plr", TOK_PLR }, + { "noerror", TOK_NOERROR }, + { "buckets", TOK_BUCKETS }, + { "dst-ip", TOK_DSTIP }, + { "src-ip", TOK_SRCIP }, + { "dst-port", TOK_DSTPORT }, + { "src-port", TOK_SRCPORT }, + { "proto", TOK_PROTO }, + { "weight", TOK_WEIGHT }, + { "all", TOK_ALL }, + { "mask", TOK_MASK }, + { "droptail", TOK_DROPTAIL }, + { "red", TOK_RED }, + { "gred", TOK_GRED }, + { "bw", TOK_BW }, + { "bandwidth", TOK_BW }, + { "delay", TOK_DELAY }, + { "pipe", TOK_PIPE }, + { "queue", TOK_QUEUE }, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, + { "dummynet-params", TOK_NULL }, + { NULL, 0 } /* terminator */ +}; + +static void show_usage(void); + + +void n2mask(struct in6_addr *, int ); +unsigned long long align_uint64(const uint64_t *); + +/* n2mask sets n bits of the mask */ +void +n2mask(struct in6_addr *mask, int n) +{ + static int minimask[9] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + u_char *p; + + memset(mask, 0, sizeof(struct in6_addr)); + p = (u_char *) mask; + for (; n > 0; p++, n -= 8) { + if (n >= 8) + *p = 0xff; + else + *p = minimask[n]; + } + return; +} + +/* + * The following is used to generate a printable argument for + * 64-bit numbers, irrespective of platform alignment and bit size. + * Because all the printf in this program use %llu as a format, + * we just return an unsigned long long, which is larger than + * we need in certain cases, but saves the hassle of using + * PRIu64 as a format specifier. + * We don't care about inlining, this is not performance critical code. + */ +unsigned long long +align_uint64(const uint64_t *pll) +{ + uint64_t ret; + + bcopy (pll, &ret, sizeof(ret)); + return ret; +} + +/* + * conditionally runs the command. + */ +static int +do_cmd(int optname, void *optval, socklen_t *optlen) +{ + static int s = -1; /* the socket */ + int i; + + if (test_only) + return 0; + + if (s == -1) + s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (s < 0) + err(EX_UNAVAILABLE, "socket"); + + if (optname == IP_DUMMYNET_GET) + i = getsockopt(s, IPPROTO_IP, optname, optval, optlen); + else + i = setsockopt(s, IPPROTO_IP, optname, optval, optlen ? *optlen : 0); + return i; +} + +/** + * match_token takes a table and a string, returns the value associated + * with the string (-1 in case of failure). + */ +static int +match_token(struct _s_x *table, char *string) +{ + struct _s_x *pt; + size_t i = strlen(string); + + for (pt = table ; i && pt->s != NULL ; pt++) + if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) + return pt->x; + return -1; +}; + +static int +sort_q(const void *pa, const void *pb) +{ + int rev = (do_sort < 0); + int field = rev ? -do_sort : do_sort; + long long res = 0; + const struct dn_flow_queue *a = pa; + const struct dn_flow_queue *b = pb; + + switch (field) { + case 1: /* pkts */ + res = a->len - b->len; + break; + case 2: /* bytes */ + res = a->len_bytes - b->len_bytes; + break; + + case 3: /* tot pkts */ + res = a->tot_pkts - b->tot_pkts; + break; + + case 4: /* tot bytes */ + res = a->tot_bytes - b->tot_bytes; + break; + } + if (res < 0) + res = -1; + if (res > 0) + res = 1; + return (int)(rev ? res : -res); +} + +static void +list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) +{ + int l; + int index_printed = 0, indexes = 0; + char buff[255]; + struct protoent *pe; + + printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", + fs->flow_mask.proto, + fs->flow_mask.src_ip, fs->flow_mask.src_port, + fs->flow_mask.dst_ip, fs->flow_mask.dst_port); + if (fs->rq_elements == 0) + return; + + printf("BKT Prot ___Source IP/port____ " + "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); + if (do_sort != 0) + heapsort(q, fs->rq_elements, sizeof(struct dn_flow_queue), sort_q); + + /* Print IPv4 flows */ + for (l = 0; l < fs->rq_elements; l++) { + struct in_addr ina; + + /* XXX: Should check for IPv4 flows */ + if (IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) /* currently a no-op */ + printf("\n"); + indexes++; + printf(" " + "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", + fs->flow_mask.proto, + fs->flow_mask.src_ip, fs->flow_mask.src_port, + fs->flow_mask.dst_ip, fs->flow_mask.dst_port); + + printf("BKT Prot ___Source IP/port____ " + "____Dest. IP/port____ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + + printf("%3d ", q[l].hash_slot); + pe = getprotobynumber(q[l].id.proto); + if (pe) + printf("%-4s ", pe->p_name); + else + printf("%4u ", q[l].id.proto); + ina.s_addr = htonl(q[l].id.src_ip); + printf("%15s/%-5d ", + inet_ntoa(ina), q[l].id.src_port); + ina.s_addr = htonl(q[l].id.dst_ip); + printf("%15s/%-5d ", + inet_ntoa(ina), q[l].id.dst_port); + printf("%4llu %8llu %2u %4u %3u\n", + align_uint64(&q[l].tot_pkts), + align_uint64(&q[l].tot_bytes), + q[l].len, q[l].len_bytes, q[l].drops); + if (verbose) + printf(" S %20llu F %20llu\n", + align_uint64(&q[l].S), align_uint64(&q[l].F)); + } + + /* Print IPv6 flows */ + index_printed = 0; + for (l = 0; l < fs->rq_elements; l++) { + if (!IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) + printf("\n"); + indexes++; + printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", + fs->flow_mask.proto, fs->flow_mask.flow_id6); + inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6), + buff, sizeof(buff)); + printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port); + inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6), + buff, sizeof(buff) ); + printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port); + + printf("BKT ___Prot___ _flow-id_ " + "______________Source IPv6/port_______________ " + "_______________Dest. IPv6/port_______________ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + printf("%3d ", q[l].hash_slot); + pe = getprotobynumber(q[l].id.proto); + if (pe != NULL) + printf("%9s ", pe->p_name); + else + printf("%9u ", q[l].id.proto); + printf("%7d %39s/%-5d ", q[l].id.flow_id6, + inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)), + q[l].id.src_port); + printf(" %39s/%-5d ", + inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)), + q[l].id.dst_port); + printf(" %4llu %8llu %2u %4u %3u\n", + align_uint64(&q[l].tot_pkts), + align_uint64(&q[l].tot_bytes), + q[l].len, q[l].len_bytes, q[l].drops); + if (verbose) + printf(" S %20llu F %20llu\n", + align_uint64(&q[l].S), + align_uint64(&q[l].F)); + } +} + +static void +print_flowset_parms(struct dn_flow_set *fs, char *prefix) +{ + int l; + char qs[30]; + char plr[30]; + char red[90]; /* Display RED parameters */ + + l = fs->qsize; + if (fs->flags_fs & DN_QSIZE_IS_BYTES) { + if (l >= 8192) + snprintf(qs, sizeof(qs), "%d KB", l / 1024); + else + snprintf(qs, sizeof(qs), "%d B", l); + } else + snprintf(qs, sizeof(qs), "%3d sl.", l); + if (fs->plr) + snprintf(plr, sizeof(plr), "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); + else + plr[0] = '\0'; + if (fs->flags_fs & DN_IS_RED) /* RED parameters */ + snprintf(red, sizeof(red), + "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", + (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ', + 1.0 * fs->w_q / (double)(1 << SCALE_RED), + SCALE_VAL(fs->min_th), + SCALE_VAL(fs->max_th), + 1.0 * fs->max_p / (double)(1 << SCALE_RED)); + else + snprintf(red, sizeof(red), "droptail"); + + printf("%s %s%s %d queues (%d buckets) %s\n", + prefix, qs, plr, fs->rq_elements, fs->rq_size, red); +} + +static void +list_pipes(void *data, size_t nbytes, int ac, char *av[]) +{ + unsigned int rulenum; + void *next = data; + struct dn_pipe *p = (struct dn_pipe *) data; + struct dn_flow_set *fs; + struct dn_flow_queue *q; + size_t l; + + if (ac > 0) + rulenum = (unsigned int)strtoul(*av++, NULL, 10); + else + rulenum = 0; + for (; nbytes >= sizeof(struct dn_pipe); p = (struct dn_pipe *)next) { + double b = p->bandwidth; + char buf[30]; + char prefix[80]; + + if (p->next.sle_next != (struct dn_pipe *)DN_IS_PIPE) + break; /* done with pipes, now queues */ + + /* + * compute length, as pipe have variable size + */ + l = sizeof(struct dn_pipe) + p->fs.rq_elements * sizeof(struct dn_flow_queue); + next = (char *)p + l; + nbytes -= l; + + if (rulenum != 0 && rulenum != p->pipe_nr) + continue; + + /* + * Print rate (or clocking interface) + */ + if (p->if_name[0] != '\0') + snprintf(buf, sizeof(buf), "%s", p->if_name); + else if (b == 0) + snprintf(buf, sizeof(buf), "unlimited"); + else if (b >= 1000000) + snprintf(buf, sizeof(buf), "%7.3f Mbit/s", b/1000000); + else if (b >= 1000) + snprintf(buf, sizeof(buf), "%7.3f Kbit/s", b/1000); + else + snprintf(buf, sizeof(buf), "%7.3f bit/s ", b); + + snprintf(prefix, sizeof(prefix), "%05d: %s %4d ms ", + p->pipe_nr, buf, p->delay); + print_flowset_parms(&(p->fs), prefix); + if (verbose) + printf(" V %20qd\n", p->V >> MY_M); + + q = (struct dn_flow_queue *)(p+1); + list_queues(&(p->fs), q); + } + for (fs = next; nbytes >= sizeof *fs; fs = next) { + char prefix[80]; + + if (fs->next.sle_next != (struct dn_flow_set *)DN_IS_QUEUE) + break; + l = sizeof(struct dn_flow_set) + fs->rq_elements * sizeof(struct dn_flow_queue); + next = (char *)fs + l; + nbytes -= l; + q = (struct dn_flow_queue *)(fs+1); + snprintf(prefix, sizeof(prefix), "q%05d: weight %d pipe %d ", + fs->fs_nr, fs->weight, fs->parent_nr); + print_flowset_parms(fs, prefix); + list_queues(fs, q); + } +} + +static void +list(int ac, char *av[], int show_counters) +{ + void *data = NULL; + socklen_t nbytes; + int exitval = EX_OK; + + int nalloc = 1024; /* start somewhere... */ + + if (test_only) { + fprintf(stderr, "Testing only, list disabled\n"); + return; + } + + ac--; + av++; + + /* get rules or pipes from kernel, resizing array as necessary */ + nbytes = nalloc; + + while (nbytes >= nalloc) { + nalloc = nalloc * 2 + 200; + nbytes = nalloc; + if ((data = realloc(data, nbytes)) == NULL) + err(EX_OSERR, "realloc"); + + if (do_cmd(IP_DUMMYNET_GET, data, &nbytes) < 0) { + if (errno == ENOBUFS) { + nbytes = 0; + break; + } + err(EX_OSERR, "getsockopt(IP_DUMMYNET_GET)"); + + } + } + + list_pipes(data, nbytes, ac, av); + + free(data); + + if (exitval != EX_OK) + exit(exitval); +} + +static void +show_usage(void) +{ + fprintf(stderr, "usage: dnctl [options]\n" + "do \"dnctl -h\" or see dnctl manpage for details\n" + ); + exit(EX_USAGE); +} + +static void +help(void) +{ + fprintf(stderr, + "dnclt [-acdeftTnNpqS] <command> where <command> is one of:\n" + "{pipe|queue} N config PIPE-BODY\n" + "[pipe|queue] {zero|delete|show} [N{,N}]\n" + ); + exit(0); +} + +static void +delete(int ac, char *av[]) +{ + struct dn_pipe p; + int i; + int exitval = EX_OK; + socklen_t len; + + memset(&p, 0, sizeof(struct dn_pipe)); + + av++; ac--; + + while (ac && isdigit(**av)) { + i = atoi(*av); av++; ac--; + + if (do_pipe == 1) + p.pipe_nr = i; + else + p.fs.fs_nr = i; + len = sizeof(struct dn_pipe); + i = do_cmd(IP_DUMMYNET_DEL, &p, &len); + if (i) { + exitval = 1; + warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", + do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr); + } + } + if (exitval != EX_OK) + exit(exitval); +} + +/* + * the following macro returns an error message if we run out of + * arguments. + */ +#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} +#define NEED2(msg, arg) {if (!ac) errx(EX_USAGE, msg, arg);} + +static void +config_pipe(int ac, char **av) +{ + struct dn_pipe p; + int i; + char *end; + void *par = NULL; + socklen_t len; + + memset(&p, 0, sizeof(struct dn_pipe)); + + av++; ac--; + /* Pipe number */ + if (ac && isdigit(**av)) { + i = atoi(*av); av++; ac--; + if (do_pipe == 1) + p.pipe_nr = i; + else + p.fs.fs_nr = i; + } + while (ac > 0) { + double d; + int tok = match_token(dummynet_params, *av); + ac--; av++; + + switch(tok) { + case TOK_NOERROR: + p.fs.flags_fs |= DN_NOERROR; + break; + + case TOK_PLR: + NEED1("plr needs argument 0..1\n"); + d = strtod(av[0], NULL); + if (d > 1) + d = 1; + else if (d < 0) + d = 0; + p.fs.plr = (int)(d*0x7fffffff); + ac--; av++; + break; + + case TOK_QUEUE: + NEED1("queue needs queue size\n"); + end = NULL; + p.fs.qsize = (int)strtoul(av[0], &end, 0); + if (*end == 'K' || *end == 'k') { + p.fs.flags_fs |= DN_QSIZE_IS_BYTES; + p.fs.qsize *= 1024; + } else if (*end == 'B' || !strncmp(end, "by", 2)) { + p.fs.flags_fs |= DN_QSIZE_IS_BYTES; + } + ac--; av++; + break; + + case TOK_BUCKETS: + NEED1("buckets needs argument\n"); + p.fs.rq_size = (int)strtoul(av[0], NULL, 0); + ac--; av++; + break; + + case TOK_MASK: + NEED1("mask needs mask specifier\n"); + /* + * per-flow queue, mask is dst_ip, dst_port, + * src_ip, src_port, proto measured in bits + */ + par = NULL; + + p.fs.flow_mask.dst_ip = 0; + p.fs.flow_mask.src_ip = 0; + p.fs.flow_mask.dst_port = 0; + p.fs.flow_mask.src_port = 0; + p.fs.flow_mask.proto = 0; + end = NULL; + + while (ac >= 1) { + uint32_t *p32 = NULL; + uint16_t *p16 = NULL; + struct in6_addr *pa6 = NULL; + uint32_t a; + + tok = match_token(dummynet_params, *av); + ac--; av++; + switch(tok) { + case TOK_ALL: + /* + * special case, all bits significant + */ + p.fs.flow_mask.dst_ip = ~0; + p.fs.flow_mask.src_ip = ~0; + p.fs.flow_mask.dst_port = ~0; + p.fs.flow_mask.src_port = ~0; + p.fs.flow_mask.proto = ~0; + n2mask(&(p.fs.flow_mask.dst_ip6), 128); + n2mask(&(p.fs.flow_mask.src_ip6), 128); + p.fs.flags_fs |= DN_HAVE_FLOW_MASK; + goto end_mask; + + case TOK_DSTIP: + p32 = &p.fs.flow_mask.dst_ip; + break; + + case TOK_SRCIP: + p32 = &p.fs.flow_mask.src_ip; + break; + + case TOK_DSTIP6: + pa6 = &(p.fs.flow_mask.dst_ip6); + break; + + case TOK_SRCIP6: + pa6 = &(p.fs.flow_mask.src_ip6); + break; + + case TOK_DSTPORT: + p16 = &p.fs.flow_mask.dst_port; + break; + + case TOK_SRCPORT: + p16 = &p.fs.flow_mask.src_port; + break; + + case TOK_PROTO: + break; + + default: + ac++; av--; /* backtrack */ + goto end_mask; + } + if (ac < 1) + errx(EX_USAGE, "mask: value missing"); + if (*av[0] == '/') { + a = (int)strtoul(av[0]+1, &end, 0); + if (pa6 == NULL) + a = (a == 32) ? ~0 : (1 << a) - 1; + } else + a = (int)strtoul(av[0], &end, 0); + if (p32 != NULL) + *p32 = a; + else if (p16 != NULL) { + if (a > 65535) + errx(EX_DATAERR, + "mask: must be 16 bit"); + *p16 = (uint16_t)a; + } else if (pa6 != NULL) { + if (a > 128) + errx(EX_DATAERR, + "in6addr invalid mask len"); + else + n2mask(pa6, a); + } else { + if (a > 255) + errx(EX_DATAERR, + "mask: must be 8 bit"); + p.fs.flow_mask.proto = (uint8_t)a; + } + if (a != 0) + p.fs.flags_fs |= DN_HAVE_FLOW_MASK; + ac--; av++; + } /* end while, config masks */ +end_mask: + break; + + case TOK_RED: + case TOK_GRED: + NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); + p.fs.flags_fs |= DN_IS_RED; + if (tok == TOK_GRED) + p.fs.flags_fs |= DN_IS_GENTLE_RED; + /* + * the format for parameters is w_q/min_th/max_th/max_p + */ + if ((end = strsep(&av[0], "/"))) { + double w_q = strtod(end, NULL); + if (w_q > 1 || w_q <= 0) + errx(EX_DATAERR, "0 < w_q <= 1"); + p.fs.w_q = (int) (w_q * (1 << SCALE_RED)); + } + if ((end = strsep(&av[0], "/"))) { + p.fs.min_th = (int)strtoul(end, &end, 0); + if (*end == 'K' || *end == 'k') + p.fs.min_th *= 1024; + } + if ((end = strsep(&av[0], "/"))) { + p.fs.max_th = (int)strtoul(end, &end, 0); + if (*end == 'K' || *end == 'k') + p.fs.max_th *= 1024; + } + if ((end = strsep(&av[0], "/"))) { + double max_p = strtod(end, NULL); + if (max_p > 1 || max_p <= 0) + errx(EX_DATAERR, "0 < max_p <= 1"); + p.fs.max_p = (int)(max_p * (1 << SCALE_RED)); + } + ac--; av++; + break; + + case TOK_DROPTAIL: + p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); + break; + + case TOK_BW: + NEED1("bw needs bandwidth or interface\n"); + if (do_pipe != 1) + errx(EX_DATAERR, "bandwidth only valid for pipes"); + /* + * set clocking interface or bandwidth value + */ + if (av[0][0] >= 'a' && av[0][0] <= 'z') { + /* interface name */ + strlcpy(p.if_name, av[0], sizeof(p.if_name)); + p.bandwidth = 0; + } else { + p.if_name[0] = '\0'; + p.bandwidth = (int)strtoul(av[0], &end, 0); + if (*end == 'K' || *end == 'k') { + end++; + p.bandwidth *= 1000; + } else if (*end == 'M') { + end++; + p.bandwidth *= 1000000; + } + if (*end == 'B' || !strncmp(end, "by", 2)) + p.bandwidth *= 8; + if (p.bandwidth < 0) + errx(EX_DATAERR, "bandwidth too large"); + } + ac--; av++; + break; + + case TOK_DELAY: + if (do_pipe != 1) + errx(EX_DATAERR, "delay only valid for pipes"); + NEED2("delay needs argument 0..%d\n", MAX_DELAY); + p.delay = (int)strtoul(av[0], NULL, 0); + ac--; av++; + break; + + case TOK_WEIGHT: + if (do_pipe == 1) + errx(EX_DATAERR,"weight only valid for queues"); + NEED1("weight needs argument 0..100\n"); + p.fs.weight = (int)strtoul(av[0], &end, 0); + ac--; av++; + break; + + case TOK_PIPE: + if (do_pipe == 1) + errx(EX_DATAERR,"pipe only valid for queues"); + NEED1("pipe needs pipe_number\n"); + p.fs.parent_nr = strtoul(av[0], &end, 0); + ac--; av++; + break; + + default: + errx(EX_DATAERR, "unrecognised option ``%s''", *(--av)); + } + } + if (do_pipe == 1) { + if (p.pipe_nr == 0) + errx(EX_DATAERR, "pipe_nr must be > 0"); + if (p.delay > MAX_DELAY) + errx(EX_DATAERR, "delay must be < %d ms", MAX_DELAY); + } else { /* do_pipe == 2, queue */ + if (p.fs.parent_nr == 0) + errx(EX_DATAERR, "pipe must be > 0"); + if (p.fs.weight >100) + errx(EX_DATAERR, "weight must be <= 100"); + } + if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) { + if (p.fs.qsize > 1024*1024) + errx(EX_DATAERR, "queue size must be < 1MB"); + } else { + if (p.fs.qsize > 100) + errx(EX_DATAERR, "2 <= queue size <= 100"); + } + if (p.fs.flags_fs & DN_IS_RED) { + size_t len; + int lookup_depth, avg_pkt_size; + double s, idle, weight, w_q; + struct clockinfo ck; + int t; + + if (p.fs.min_th >= p.fs.max_th) + errx(EX_DATAERR, "min_th %d must be < than max_th %d", + p.fs.min_th, p.fs.max_th); + if (p.fs.max_th == 0) + errx(EX_DATAERR, "max_th must be > 0"); + + len = sizeof(int); + if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", + &lookup_depth, &len, NULL, 0) == -1) + + errx(1, "sysctlbyname(\"%s\")", + "net.inet.ip.dummynet.red_lookup_depth"); + if (lookup_depth == 0) + errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" + " must be greater than zero"); + + len = sizeof(int); + if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", + &avg_pkt_size, &len, NULL, 0) == -1) + + errx(1, "sysctlbyname(\"%s\")", + "net.inet.ip.dummynet.red_avg_pkt_size"); + if (avg_pkt_size == 0) + errx(EX_DATAERR, + "net.inet.ip.dummynet.red_avg_pkt_size must" + " be greater than zero"); + + len = sizeof(struct clockinfo); + if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1) + errx(1, "sysctlbyname(\"%s\")", "kern.clockrate"); + + /* + * Ticks needed for sending a medium-sized packet. + * Unfortunately, when we are configuring a WF2Q+ queue, we + * do not have bandwidth information, because that is stored + * in the parent pipe, and also we have multiple queues + * competing for it. So we set s=0, which is not very + * correct. But on the other hand, why do we want RED with + * WF2Q+ ? + */ + if (p.bandwidth==0) /* this is a WF2Q+ queue */ + s = 0; + else + s = ck.hz * avg_pkt_size * 8 / p.bandwidth; + + /* + * max idle time (in ticks) before avg queue size becomes 0. + * NOTA: (3/w_q) is approx the value x so that + * (1-w_q)^x < 10^-3. + */ + w_q = ((double)p.fs.w_q) / (1 << SCALE_RED); + idle = s * 3. / w_q; + p.fs.lookup_step = (int)idle / lookup_depth; + if (!p.fs.lookup_step) + p.fs.lookup_step = 1; + weight = 1 - w_q; + for (t = p.fs.lookup_step; t > 0; --t) + weight *= weight; + p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED)); + } + len = sizeof(struct dn_pipe); + i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, &len); + if (i) + err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); +} + +static void +flush(int force) +{ + if (!force && !do_quiet) { /* need to ask user */ + int c; + + printf("Are you sure? [yn] "); + fflush(stdout); + do { + c = toupper(getc(stdin)); + while (c != '\n' && getc(stdin) != '\n') + if (feof(stdin)) + return; /* and do not flush */ + } while (c != 'Y' && c != 'N'); + printf("\n"); + if (c == 'N') /* user said no */ + return; + } + + if (do_cmd(IP_DUMMYNET_FLUSH, NULL, 0) < 0) + err(EX_UNAVAILABLE, "setsockopt(IP_DUMMYNET_FLUSH)"); + + if (!do_quiet) + printf("Flushed all pipes.\n"); +} + +/* + * Free a the (locally allocated) copy of command line arguments. + */ +static void +free_args(int ac, char **av) +{ + int i; + + for (i=0; i < ac; i++) + free(av[i]); + free(av); +} + +/* + * Called with the arguments (excluding program name). + * Returns 0 if successful, 1 if empty command, errx() in case of errors. + */ +static int +parse_args(int oldac, char **oldav) +{ + int ch, ac, save_ac; + char **av, **save_av; + int do_acct = 0; /* Show packet/byte count */ + int do_force = 0; /* Don't ask for confirmation */ + +#define WHITESP " \t\f\v\n\r" + if (oldac == 0) + return 1; + else if (oldac == 1) { + /* + * If we are called with a single string, try to split it into + * arguments for subsequent parsing. + * But first, remove spaces after a ',', by copying the string + * in-place. + */ + char *arg = oldav[0]; /* The string... */ + size_t l = strlen(arg); + int copy = 0; /* 1 if we need to copy, 0 otherwise */ + int i, j; + for (i = j = 0; i < l; i++) { + if (arg[i] == '#') /* comment marker */ + break; + if (copy) { + arg[j++] = arg[i]; + copy = !index("," WHITESP, arg[i]); + } else { + copy = !index(WHITESP, arg[i]); + if (copy) + arg[j++] = arg[i]; + } + } + if (!copy && j > 0) /* last char was a 'blank', remove it */ + j--; + l = j; /* the new argument length */ + arg[j++] = '\0'; + if (l == 0) /* empty string! */ + return 1; + + /* + * First, count number of arguments. Because of the previous + * processing, this is just the number of blanks plus 1. + */ + for (i = 0, ac = 1; i < l; i++) + if (index(WHITESP, arg[i]) != NULL) + ac++; + + av = calloc(ac, sizeof(char *)); + + /* + * Second, copy arguments from cmd[] to av[]. For each one, + * j is the initial character, i is the one past the end. + */ + for (ac = 0, i = j = 0; i < l; i++) + if (index(WHITESP, arg[i]) != NULL || i == l-1) { + if (i == l-1) + i++; + av[ac] = calloc(i-j+1, 1); + bcopy(arg+j, av[ac], i-j); + ac++; + j = i + 1; + } + } else { + /* + * If an argument ends with ',' join with the next one. + * Just add its length to 'l' and continue. When we have a string + * without a ',' ending, we'll have the combined length in 'l' + */ + int first, i; + size_t l; + + av = calloc(oldac, sizeof(char *)); + for (first = i = ac = 0, l = 0; i < oldac; i++) { + char *arg = oldav[i]; + size_t k = strlen(arg); + + l += k; + if (arg[k-1] != ',' || i == oldac-1) { + size_t buflen = l+1; + /* Time to copy. */ + av[ac] = calloc(l+1, 1); + for (l=0; first <= i; first++) { + strlcat(av[ac]+l, oldav[first], buflen-l); + l += strlen(oldav[first]); + } + ac++; + l = 0; + first = i+1; + } + } + } + + /* Set the force flag for non-interactive processes */ + do_force = !isatty(STDIN_FILENO); + + /* Save arguments for final freeing of memory. */ + save_ac = ac; + save_av = av; + + optind = optreset = 0; + while ((ch = getopt(ac, av, "afhnqsv")) != -1) + switch (ch) { + case 'a': + do_acct = 1; + break; + + case 'f': + do_force = 1; + break; + + case 'h': /* help */ + free_args(save_ac, save_av); + help(); + break; /* NOTREACHED */ + + case 'n': + test_only = 1; + break; + + case 'q': + do_quiet = 1; + break; + + case 's': /* sort */ + do_sort = atoi(optarg); + break; + + case 'v': /* verbose */ + verbose = 1; + break; + + default: + free_args(save_ac, save_av); + return 1; + } + + ac -= optind; + av += optind; + NEED1("bad arguments, for usage summary ``dnctl''"); + + /* + * An undocumented behaviour of dnctl1 was to allow rule numbers first, + * e.g. "100 add allow ..." instead of "add 100 allow ...". + * In case, swap first and second argument to get the normal form. + */ + if (ac > 1 && isdigit(*av[0])) { + char *p = av[0]; + + av[0] = av[1]; + av[1] = p; + } + + /* + * optional: pipe or queue + */ + do_pipe = 0; + if (!strncmp(*av, "pipe", strlen(*av))) + do_pipe = 1; + else if (!strncmp(*av, "queue", strlen(*av))) + do_pipe = 2; + if (do_pipe) { + ac--; + av++; + } + NEED1("missing command"); + + /* + * For pipes and queues we normally say 'pipe NN config' + * but the code is easier to parse as 'pipe config NN' + * so we swap the two arguments. + */ + if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) { + char *p = av[0]; + + av[0] = av[1]; + av[1] = p; + } + + if (do_pipe && !strncmp(*av, "config", strlen(*av))) + config_pipe(ac, av); + else if (!strncmp(*av, "delete", strlen(*av))) + delete(ac, av); + else if (!strncmp(*av, "flush", strlen(*av))) + flush(do_force); + else if (!strncmp(*av, "print", strlen(*av)) || + !strncmp(*av, "list", strlen(*av))) + list(ac, av, do_acct); + else if (!strncmp(*av, "show", strlen(*av))) + list(ac, av, 1 /* show counters */); + else + errx(EX_USAGE, "bad command `%s'", *av); + + /* Free memory allocated in the argument parsing. */ + free_args(save_ac, save_av); + return 0; +} + +static void +dnctl_readfile(int ac, char *av[]) +{ +#define MAX_ARGS 32 + char buf[BUFSIZ]; + char *cmd = NULL, *filename = av[ac-1]; + int c, lineno=0; + FILE *f = NULL; + pid_t preproc = 0; + + while ((c = getopt(ac, av, "np:q")) != -1) { + switch(c) { + case 'n': + test_only = 1; + break; + + case 'p': + cmd = optarg; + /* + * Skip previous args and delete last one, so we + * pass all but the last argument to the preprocessor + * via av[optind-1] + */ + av += optind - 1; + ac -= optind - 1; + av[ac-1] = NULL; + fprintf(stderr, "command is %s\n", av[0]); + break; + + case 'q': + do_quiet = 1; + break; + + default: + errx(EX_USAGE, "bad arguments, for usage" + " summary ``dnctl''"); + } + + if (cmd != NULL) + break; + } + + if (cmd == NULL && ac != optind + 1) { + fprintf(stderr, "ac %d, optind %d\n", ac, optind); + errx(EX_USAGE, "extraneous filename arguments"); + } + + if ((f = fopen(filename, "r")) == NULL) + err(EX_UNAVAILABLE, "fopen: %s", filename); + + if (cmd != NULL) { /* pipe through preprocessor */ + int pipedes[2]; + + if (pipe(pipedes) == -1) + err(EX_OSERR, "cannot create pipe"); + + preproc = fork(); + if (preproc == -1) + err(EX_OSERR, "cannot fork"); + + if (preproc == 0) { + /* + * Child, will run the preprocessor with the + * file on stdin and the pipe on stdout. + */ + if (dup2(fileno(f), 0) == -1 + || dup2(pipedes[1], 1) == -1) + err(EX_OSERR, "dup2()"); + fclose(f); + close(pipedes[1]); + close(pipedes[0]); + execvp(cmd, av); + err(EX_OSERR, "execvp(%s) failed", cmd); + } else { /* parent, will reopen f as the pipe */ + fclose(f); + close(pipedes[1]); + if ((f = fdopen(pipedes[0], "r")) == NULL) { + int savederrno = errno; + + (void)kill(preproc, SIGTERM); + errno = savederrno; + err(EX_OSERR, "fdopen()"); + } + } + } + + while (fgets(buf, BUFSIZ, f)) { /* read commands */ + char linename[16]; + char *args[1]; + + lineno++; + snprintf(linename, sizeof(linename), "Line %d", lineno); + setprogname(linename); /* XXX */ + args[0] = buf; + parse_args(1, args); + } + fclose(f); + if (cmd != NULL) { + int status; + + if (waitpid(preproc, &status, 0) == -1) + errx(EX_OSERR, "waitpid()"); + if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) + errx(EX_UNAVAILABLE, + "preprocessor exited with status %d", + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + errx(EX_UNAVAILABLE, + "preprocessor exited with signal %d", + WTERMSIG(status)); + } +} + +int +main(int ac, char *av[]) +{ + /* + * If the last argument is an absolute pathname, interpret it + * as a file to be preprocessed. + */ + + if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) + dnctl_readfile(ac, av); + else { + if (parse_args(ac-1, av+1)) + show_usage(); + } + return EX_OK; +} diff --git a/network_cmds/ecnprobe/base.h b/network_cmds/ecnprobe/base.h new file mode 100644 index 0000000..562b4ef --- /dev/null +++ b/network_cmds/ecnprobe/base.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <stdio.h> +#include <ctype.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> + +#include <assert.h> diff --git a/network_cmds/ecnprobe/capture.c b/network_cmds/ecnprobe/capture.c new file mode 100644 index 0000000..32a8e0b --- /dev/null +++ b/network_cmds/ecnprobe/capture.c @@ -0,0 +1,204 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "gmt2local.h" +#include "pcap.h" +#include "inet.h" +#include "capture.h" + +/* set snaplen to max etherenet packet size */ +#define DEFAULT_SNAPLEN 1500 + +pcap_t *pc; /* pcap device */ +int datalinkOffset; /* offset of ip packet from datalink packet */ +int captureDebug = 1; +unsigned int thisTimeZone; + +void CaptureInit(u_int32_t sourceIP, u_int16_t sourcePort, + u_int32_t targetIP, u_int16_t targetPort, char *dev) +{ + + char *device = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; + int snaplen = DEFAULT_SNAPLEN; + int promisc = 1; + int timeout = 10; /* timeout in 1 second (10 ms) */ + char filtercmds[255]; + bpf_u_int32 netmask = 0; + struct bpf_program filter; + char source[18]; + char target[18]; + int i; + + /* Get local time zone for interpreting timestamps */ + /* XXX - does this belong here? */ + thisTimeZone = gmt2local(0); + + if (dev != NULL) { + device = dev; + } else { + pcap_if_t *devlist; + /* + * Find the list of interfaces, and pick + * the first interface. + */ + if (pcap_findalldevs(&devlist, errbuf) >= 0 && + devlist != NULL) { + device = strdup(devlist->name); + pcap_freealldevs(devlist); + } + + if (device == NULL) { + fprintf(stderr, "Can't find capture device: %s\n", errbuf); + exit(-1); + } + } + + if (captureDebug) { + printf("Device name is %s\n", device); + } + pc = pcap_open_live(device, snaplen, promisc, timeout, errbuf); + if (pc == NULL) { + fprintf(stderr,"Can't open capture device %s: %s\n",device, errbuf); + exit(-1); + } + + /* XXX why do we need to do this? */ + i = pcap_snapshot(pc); + if (snaplen < i) { + fprintf(stderr, "Warning: snaplen raised to %d from %d", + snaplen, i); + } + + if ((i = pcap_datalink(pc)) < 0) { + fprintf(stderr,"Unable to determine datalink type for %s: %s\n", + device, errbuf); + exit(-1); + } + + switch(i) { + + case DLT_EN10MB: datalinkOffset = 14; break; + case DLT_IEEE802: datalinkOffset = 22; break; + case DLT_NULL: datalinkOffset = 4; break; + case DLT_SLIP: + case DLT_PPP: datalinkOffset = 24; break; + case DLT_RAW: datalinkOffset = 0; break; + default: + fprintf(stderr,"Unknown datalink type %d\n",i); + exit(-1); + break; + + } + + if (InetAddress(sourceIP) < 0) { + fprintf(stderr, "Invalid source IP address (%d)\n", sourceIP); + exit(-1); + } + + strlcpy(source, InetAddress(sourceIP), sizeof(source)); + strlcpy(target, InetAddress(targetIP), sizeof(target)); + + /* Setup initial filter */ + sprintf(filtercmds, + "(host %s && host %s && port %d) || icmp\n", + source, target, targetPort); + + if (captureDebug) { + printf("datalinkOffset = %d\n", datalinkOffset); + printf("filter = %s\n", filtercmds); + } + if (pcap_compile(pc, &filter, filtercmds, 1, netmask) < 0) { + printf("Error: %s", pcap_geterr(pc)); + exit(-1); + } + + if (pcap_setfilter(pc, &filter) < 0) { + fprintf(stderr, "Can't set filter: %s",pcap_geterr(pc)); + exit(-1); + } + + if (captureDebug) { + printf("Listening on %s...\n", device); + } + +} + +char *CaptureGetPacket(struct pcap_pkthdr *pi) +{ + + const u_char *p; + + p = pcap_next(pc, (struct pcap_pkthdr *)pi); + + if (p != NULL) { + p += datalinkOffset; + } + + pi->ts.tv_sec = (pi->ts.tv_sec + thisTimeZone) % 86400; + + return (char *)p; + +} + + +void CaptureEnd() +{ + struct pcap_stat stat; + + if (pcap_stats(pc, &stat) < 0) { + (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pc)); + } + else { + (void)fprintf(stderr, "%d packets received by filter\n", stat.ps_recv); + (void)fprintf(stderr, "%d packets dropped by kernel\n", stat.ps_drop); + } + + pcap_close(pc); +} + diff --git a/network_cmds/ecnprobe/capture.h b/network_cmds/ecnprobe/capture.h new file mode 100644 index 0000000..9bb83be --- /dev/null +++ b/network_cmds/ecnprobe/capture.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#ifndef _CAPTURE_H_ +#define _CAPTURE_H_ +#include <pcap/pcap.h> + +struct PacketInfo { + struct timeval ts; + unsigned int caplen; + unsigned int len; +}; + +void CaptureInit(u_int32_t sourceIP, u_int16_t sourcePort, + u_int32_t targetIP, u_int16_t targetPort, char *dev); +char *CaptureGetPacket(struct pcap_pkthdr *); +void CaptureEnd(); + +#endif /* _CAPTURE_H_ */ diff --git a/network_cmds/ecnprobe/ecn.c b/network_cmds/ecnprobe/ecn.c new file mode 100644 index 0000000..b7dabdf --- /dev/null +++ b/network_cmds/ecnprobe/ecn.c @@ -0,0 +1,1119 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" +#include "history.h" +#include "ecn.h" + +extern struct TcpSession session; +extern struct History history[]; + +#define ESTABLISH_SUCCESS 0 +#define ESTABLISH_FAILURE_EARLY_RST 1 +#define ESTABLISH_FAILURE_NO_REPLY 2 +int EstablishTcpConnection(u_int8_t syn_flags, u_int8_t ip_tos) +{ + struct IPPacket *synPacket = NULL, *ackPacket = NULL; + char *read_packet; + struct pcap_pkthdr pi; + int synAckReceived = 0; + int numRetransmits = 0; + double timeoutTime; + int tcpoptlen = 4; /* For negotiating MSS */ + u_int8_t *opt = NULL; + struct IPPacket *p = NULL; + + /* allocate the syn packet -- Changed for new IPPacket structure */ + synPacket = AllocateIPPacket(0, tcpoptlen, 0, "ECN (SYN)"); + opt = (((u_int8_t *)synPacket->tcp) + sizeof(struct TcpHeader)); + opt[0] = (u_int8_t)TCPOPT_MAXSEG; + opt[1] = (u_int8_t)TCPOLEN_MAXSEG; + *((u_int16_t *)((u_int8_t *)opt + 2)) = htons(session.mss); + + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN | syn_flags, 0, tcpoptlen, ip_tos); + timeoutTime = GetTime() + 1; + + /* + * Wait for SYN/ACK and retransmit SYN if appropriate + * not great, but it gets the job done + */ + + while(!synAckReceived && numRetransmits < 3) { + while(GetTime() < timeoutTime) { + /* Have we captured any packets? */ + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + /* Received a packet from us to them */ + if (INSESSION(p, session.src, session.sport, + session.dst, session.dport)) { + /* Is it a SYN/ACK? */ + if (p->tcp->tcp_flags & TCPFLAGS_SYN) { + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + session.totSeenSent++ ; + } else { + processBadPacket(p); + } + continue; + } + + /* Received a packet from them to us */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport)) { + /* Is it a SYN/ACK? */ + if ((p->tcp->tcp_flags & TCPFLAGS_SYN) && + (p->tcp->tcp_flags & TCPFLAGS_ACK)) { + timeoutTime = GetTime(); /* force exit */ + synAckReceived++; + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + + /* + * Save ttl for,admittedly poor,indications of reverse + * route change + */ + session.ttl = p->ip->ip_ttl; + session.snd_wnd = ntohl(p->tcp->tcp_win); + session.totRcvd ++; + break; + } else { + if ((p->tcp->tcp_flags)& (TCPFLAGS_RST)) { + printf ("ERROR: EARLY_RST\n"); + return(ESTABLISH_FAILURE_EARLY_RST); + } + } + } + } + } + + if (!synAckReceived) { + if (session.debug >= SESSION_DEBUG_LOW) { + printf("SYN timeout. Retransmitting\n"); + } + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN | syn_flags, 0, tcpoptlen, ip_tos); + timeoutTime = GetTime() + 1; + numRetransmits++; + } + } + + if (numRetransmits >= 3) { + printf("ERROR: No connection after 3 retries...\nRETURN CODE: %d\n", + NO_CONNECTION); + return(ESTABLISH_FAILURE_NO_REPLY); + } + if (session.debug >= SESSION_DEBUG_LOW) + printf("Received SYN-ACK, try to send the third Ack\n"); + /* Update session variables */ + session.irs = ntohl(p->tcp->tcp_seq); + session.dataRcvd[0] = 1 ; + session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */ + session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */ + session.snd_una = session.iss + 1; + session.maxseqseen = ntohl(p->tcp->tcp_seq); + session.initSession = 1; + if (session.debug >= SESSION_DEBUG_LOW) { + printf("src = %s:%d (%u)\n", InetAddress(session.src), + session.sport, session.iss); + printf("dst = %s:%d (%u)\n",InetAddress(session.dst), + session.dport, session.irs); + } + + /* allocate the syn packet -- Changed for new IPPacket structure */ + ackPacket = AllocateIPPacket(0, 0, 0, "Third ACK"); + /* send an ACK */ + SendSessionPacket(ackPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + TCPFLAGS_ACK, 0, 0, 0); + FreeIPPacket(&synPacket); + FreeIPPacket(&ackPacket); + return(ESTABLISH_SUCCESS); +} + +void ECNTest(u_int32_t sourceAddress, u_int16_t sourcePort, + u_int32_t targetAddress, u_int16_t targetPort, int mss) +{ + int rawSocket, rc, flag = 1; + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = 5*mss; + /* random initial sequence number */ + session.snd_nxt = arc4random(); + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss; + session.maxseqseen = 0; + session.epochTime = GetTime (); + session.maxpkts = 1000; +/* session.debug = SESSION_DEBUG_LOW; */ + session.debug = 0; + if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t), + mss * session.maxpkts)) == NULL) { + printf("no memmory to store data:\nRETURN CODE: %d", + ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + Quit(ERR_SOCKET_OPEN); + } + + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, + (char *)&flag, sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + /* Establish a TCP connections with ECN bits */ + rc = EstablishTcpConnection(TCPFLAGS_ECN_ECHO | TCPFLAGS_CWR, 0); + switch (rc) { + case ESTABLISH_FAILURE_EARLY_RST: + { + /* Received a RST when ECN bits are used. Try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Received RST with or without ECN negotiation\n"); + printf("The server might not be listening on this port\n"); + Quit(EARLY_RST); + } else if (rc == ESTABLISH_SUCCESS) { + printf("Received RST with ECN.\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Received RST with ECN\n"); + printf("Exceed max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } + break; + } + case ESTABLISH_FAILURE_NO_REPLY: + { + /* No reply after retring, try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Received RST without ECN\n"); + Quit(NO_CONNECTION); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Exceeded max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } else { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } + break; + } + } + + /* Test for propogation of CE correctly */ + DataPkt(session.filename, 3, 0); + + checkECN(); + return; +} + +void DataPkt (char *filename, u_int8_t iptos, u_int8_t tcp_flags) +{ + struct IPPacket *p, *datapkt; + struct pcap_pkthdr pi; + char *read_packet; + int i ; + int sendflag = 1 ; + u_int16_t lastSeqSent = session.snd_nxt; + double startTime = 0; + char *dataptr ; + char data[MAXREQUESTLEN]; + int datalen; + int ipsz; + + datalen = PrepareRequest (data, filename); + + datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)"); + + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr,(void *)data, datalen); + + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1; + + /* send the data packet + * we try to "achieve" reliability by + * sending the packet upto 5 times, wating for + * 2 seconds between packets + * BAD busy-wait loop + */ + + i = 0 ; + while(1) { + + if (sendflag == 1) { + SendSessionPacket(datapkt, ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK | tcp_flags, 0, 0, iptos); + + startTime = GetTime(); + sendflag = 0 ; + i++ ; + } + + /* Check if we have received any packets */ + if ((read_packet =(char *)CaptureGetPacket(&pi)) != NULL) { + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * packet that we sent? + */ + + if (INSESSION(p,session.src, session.sport, + session.dst,session.dport) && + (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) && + SEQ_GT(ntohl(p->tcp->tcp_seq), lastSeqSent) && + SEQ_LEQ(ntohl(p->tcp->tcp_ack), session.rcv_nxt)) { + lastSeqSent = ntohl(p->tcp->tcp_seq); + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.snd_nxt += datalen + 1; + session.totSeenSent ++ ; + continue ; + } + + /* + * from them? + */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport) && (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) && + SEQ_GT(ntohl(p->tcp->tcp_ack), session.snd_una)) { + session.snd_una = ntohl(p->tcp->tcp_ack); + session.snd_nxt = session.snd_una; + if (p->ip->ip_ttl != session.ttl) { + session.ttl = p->ip->ip_ttl; + } + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.totRcvd ++; + break ; + } + /* + * otherwise, this is a bad packet + * we must quit + */ + //processBadPacket(p); + } + if ((GetTime() - startTime >= 1) && (sendflag == 0) && (i < 3)) { + sendflag = 1 ; + } + if (i >= 3) { + printf ("ERROR: sent request 3 times without response\n"); + return; + } + } + + FreeIPPacket(&datapkt); + + /* process any response by sending Acks */ + rcvData (ECNAckData); +} + +void ECNAckData (struct IPPacket *p) +{ + + uint32 seq = history[session.hsz - 1].seqno; + uint16 datalen = history[session.hsz - 1].dlen; + int fin = history[session.hsz - 1].fin; + int rst = history[session.hsz - 1].rst; + int i; + struct IPPacket *ackpkt = NULL; + static int ECT_00 = 0; + static int ECT_01 = 0; + static int ECT_10 = 0; + static int ECT_11 = 0; + static int ECN_ECHO = 0; + static int send_cwr = 0; + static int send_ece = 0; + uint8 tcp_flags = 0; + + + /* Legend: + * ECN_ECHO: counts packets with TCP header ECN bit set + * ECT_XX: counts packets with ECT codepoint XX (IP) + */ + + if (datalen > session.mss) { + printf ("ERROR: mss=%d datalen=%d\nRETURN CODE: %d\n", + session.mss, datalen, MSS_ERR); + Quit(MSS_ERR); + } + + if (datalen > 0) { + char *http_code = (char *)calloc(4, sizeof(char)); + if (seq - session.irs == 1) { + /* Response to request packet --> check HTTP response code */ + memcpy(http_code, ((char *)(p->tcp) + sizeof(struct TcpHeader) + + history[session.hsz - 1].optlen + 9), 3); + if (strncmp(http_code, HTTP_OK, 3) != 0) { + printf("HTTP ERROR - HTTP RESPONSE CODE: %s\nRETURN CODE: %d\n", + http_code, atoi(http_code)); + Quit(atoi(http_code)); + } + } + + session.totDataPktsRcvd++; + + if (session.verbose) { + printf ("r %f %d %d\n", + GetTime() - session.epochTime, + seq - session.irs, + seq - session.irs + datalen); + } + + } + + /* Check if packet has the ECN_ECHO flag set */ + if (history[session.hsz - 1].ecn_echo) { + ECN_ECHO += 1; + } + + if ((p->ip->ip_tos & 0x17) == 0) { + ECT_00 += 1; + } + if ((p->ip->ip_tos & 0x17) == 1) { + ECT_01 += 1; + } + if ((p->ip->ip_tos & 0x17) == 2) { + ECT_10 += 1; + } + if ((p->ip->ip_tos & 0x17) == 3) { + ECT_11 += 1; + } + + if(session.maxseqseen < seq + datalen - 1) { + session.maxseqseen = seq + datalen - 1; + } else { + if (datalen > 0) { + if (reordered(p) != 1) { + session.num_unwanted_drops += 1; + } + } + } + + /* from TCP/IP vol. 2, p 808 */ + if (SEQ_LEQ(session.rcv_nxt, seq) && + SEQ_LT(seq, (session.rcv_nxt + session.rcv_wnd)) && + SEQ_LEQ(session.rcv_nxt, (seq + datalen)) && + SEQ_LT((seq+datalen-1), (session.rcv_nxt + session.rcv_wnd))) { + int start, end; + start = seq - session.irs ; + end = start + datalen ; + + for (i = start ; i < end ; i++) { + session.dataRcvd[i] = 1 ; + } + + start = session.rcv_nxt - session.irs ; + end = session.mss * session.maxpkts ; + + for (i = start ; i < end ; i++) { + if (session.dataRcvd[i] == 0) { + break ; + } + session.rcv_nxt++ ; + } + } + + if (datalen > 0) { + if (session.verbose) { + printf ("a %f %d\n", GetTime() - session.epochTime, + session.rcv_nxt - session.irs); + } + ackpkt = AllocateIPPacket(0, 0, 0, "NewECN (ACK)"); + if ((p->ip->ip_tos & 0x17) == 3) { + tcp_flags = TCPFLAGS_ACK | TCPFLAGS_ECN_ECHO; + } else { + tcp_flags = TCPFLAGS_ACK; + } + + if (send_cwr == 2 && send_ece < 2) { + /* Send ECE as if a CE was received, we have to get CWR back */ + send_ece = 1; + tcp_flags |= TCPFLAGS_ECN_ECHO; + } + + SendSessionPacket (ackpkt, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + tcp_flags, 0, 0, 0); + } + + if (send_cwr == 0 && (p->tcp->tcp_flags & TCPFLAGS_ECN_ECHO)) { + /* Send CWR atleast once if ECN ECHO is set */ + int datalen; + struct IPPacket *datapkt; + char *dataptr; + char data[MAXREQUESTLEN]; + int ipsz; + + datalen = PrepareRequest(data, NULL); + datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)"); + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr, (void *)data, datalen); + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + + datalen + 1; + + SendSessionPacket(datapkt, ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK | TCPFLAGS_CWR, 0, 0, 2); + + session.snd_nxt += (datalen + 1); + send_cwr = 1; + FreeIPPacket(&datapkt); + } + + if (send_cwr == 1 && !(p->tcp->tcp_flags & TCPFLAGS_ECN_ECHO)) { + /* ECE was reset in response to CWR, move to the next state of probing */ + send_cwr = 2; + } + + if (send_ece == 1 && (p->tcp->tcp_flags & TCPFLAGS_CWR)) { + /* Received CWR in response to ECE */ + send_ece = 2; + } + + if (SEQ_GT(ntohl(p->tcp->tcp_ack), session.snd_una)) + session.snd_una = ntohl(p->tcp->tcp_ack); + if (SEQ_LT(session.snd_nxt, session.snd_una)) + session.snd_nxt = session.snd_una; + + if (fin || rst) { + /* Increment sequence number for FIN rcvd */ + session.rcv_nxt++; + if (ECT_01 == 0 && ECT_10 == 0) { + printf("Never received ECT(0) or ECT(1) in ToS field: FAIL\n"); + } + if (ECT_11 > 3) { + /* If we received more than 3 CE, flag it as an error */ + printf("Received too many ECT_CE (%d): FAIL\n", ECT_11); + } + printf ("Totdata = %d ECN_ECHO: %d ECT00: %d ECT01: %d ECT10: %d ECT11: %d drops: %d\n", + session.rcv_nxt - session.irs, ECN_ECHO, ECT_00, + ECT_01, ECT_10, ECT_11, session.num_unwanted_drops); + if (fin) { + SendSessionPacket (ackpkt, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + tcp_flags, 0, 0, 0); + } + checkECN(); + Quit(SUCCESS); + } +} + +void checkECN () +{ + int i; + int sr = 0; /* sr=1: SYN/ACK rcvd */ + int se = 0; /* se=0: no CWR/no ECHO; se=1: no CWR/ECHO; se=2: CWR/ECHO */ + int ar = 0; /* ar=0: no ACK rcvd; ar=1: ACK rcvd */ + int ae = 0; /* ae=0: ACK/no ECHO; ae=1: ACK/ECHO */ + int we = 0; /* we=0: no ECHO; we=1 ECHO/CWR; we=2 ECHO/CWR/ECHO stop */ + int ee = 0; /* ee=0 never sent ECE; ee=1 sent ECE; ee=2 ECE / CWR */ + + for (i = 0 ; i < session.hsz; i++) { + if ((history[i].type == RCVD) && (history[i].syn == 1) && + (history[i].ack == 1)) { + sr = 1; + if (history[i].ecn_echo == 1) { + se = 1; + if (history[i].cwr == 1) { + se = 2; + } + } + } + } + + for (i = 0 ; i < session.hsz; i++) { + if (history[i].type == RCVD && history[i].syn == 0 && + history[i].ack == 1) { + ar = 1; + if (history[i].ecn_echo == 1) { + ae = 1; + } + } + } + + for (i = 0; i < session.hsz; i++) { + if (history[i].type == SENT && history[i].dlen > 0 && + history[i].cwr == 1) { + we = 1; + continue; + } + if (we == 1 && history[i].type == RCVD && history[i].ecn_echo == 0) { + we = 2; + break; + } + if (we == 2 && history[i].type == RCVD && history[i].ecn_echo == 1) { + we = 1; + break; + } + } + + for (i = 0; i < session.hsz; i++) { + if (history[i].type == SENT && history[i].ecn_echo == 1) { + ee = 1; + continue; + } + if (ee == 1 && history[i].type == RCVD && history[i].dlen > 0 && + history[i].cwr == 1) { + /* Received cwr in response to ECE */ + ee = 2; + break; + } + } + + printf ("sr=%d se=%d ar=%d ae=%d we=%d\n", sr, se, ar, ae, we); + switch (sr) { + case 0: + printf("No SYN/ACK received from server\n"); + break; + case 1: + printf("SYN/ACK received: PASS \n"); + break; + default: + printf("Unknown value for sr: %d\n", sr); + break; + } + switch (se) { + case 0: + printf("No CWR or ECE on SYN/ACK, server does not support ECN\n"); + break; + case 1: + printf("ECE flag set on SYN/ACK, server supports ECN: PASS\n"); + break; + case 2: + printf("Both CWR and ECE set on SYN/ACK, incompatible SYN/ACK\n"); + break; + default: + printf("Unknown value for se: %d\n", se); + break; + } + + switch (ar) { + case 0: + printf("No ACK received\n"); + break; + case 1: + printf("ACK received: PASS\n"); + break; + default: + printf("Unknown value for ar: %d\n", ar); + break; + } + + switch (ae) { + case 0: + printf("Received ACKS but never ECE\n"); + break; + case 1: + printf("Received ACKs with ECE, in response to simulated CE bit: PASS\n"); + break; + default: + printf("Unknown value for ae: %d\n", ae); + break; + } + + switch (we) { + case 0: + printf("Never received ECE\n"); + break; + case 1: + printf("Received ECE and sent CWR\n"); + break; + case 2: + printf("Received ECE, sent CWR and stopped receiving ECE afterwards: PASS\n"); + break; + default: + printf("Unknown value for we: %d\n", we); + break; + } + + switch (ee) { + case 0: + printf("Never sent ECE\n"); + break; + case 1: + printf("Sent ECE to simulate receiving CE \n"); + break; + case 2: + printf("Sent ECE and received CWR in response: PASS\n"); + break; + default: + printf("Unknown value for ee: %d\n", ee); + break; + } + return; +} + +void DataPktPathCheck(char *filename, u_int8_t iptos, u_int8_t tcp_flags) +{ + struct IPPacket *p, *datapkt; + struct pcap_pkthdr pi; + char *read_packet; + int i ; + int sendflag = 1 ; + u_int16_t lastSeqSent = session.snd_nxt; + double startTime = 0; + char *dataptr; + char data[MAXREQUESTLEN]; + int datalen; + int ipsz; + unsigned int init_ttl; + + datalen = PrepareRequest (data, filename); + + datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)"); + + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr,(void *)data, datalen); + + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1; + /* send the data packet + * we try to "achieve" reliability by + * sending the packet upto 5 times, wating for + * 2 seconds between packets + * BAD busy-wait loop + */ + + i = 0 ; + init_ttl = 1; + while(1) { + + if (sendflag == 1) { + session.curr_ttl = ++init_ttl; + if (init_ttl > 64) /* reached the max */ + break; + SendSessionPacket(datapkt, ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK | tcp_flags, 0, 0, 0x3); + + startTime = GetTime(); + sendflag = 0; + i++ ; + } + + /* Check if we have received any packets */ + if ((read_packet =(char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * packet that we sent? + */ + + if (INSESSION(p,session.src, session.sport, + session.dst,session.dport) && + (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) && + SEQ_GT(ntohl(p->tcp->tcp_seq), lastSeqSent) && + SEQ_LEQ(ntohl(p->tcp->tcp_ack), session.rcv_nxt)) { + lastSeqSent = ntohl(p->tcp->tcp_seq); + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.snd_nxt += datalen + 1; + session.totSeenSent ++ ; + continue ; + } + + /* + * from them? + */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport) && (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) && + ntohl(p->tcp->tcp_ack) == session.snd_nxt) { + session.snd_una = ntohl(p->tcp->tcp_ack); + session.snd_nxt = session.snd_una; + if (p->ip->ip_ttl != session.ttl) { + session.ttl = p->ip->ip_ttl; + } + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.totRcvd ++; + break ; + } + /* + * ICMP ttl exceeded + */ + if (p->ip->ip_p == IPPROTOCOL_ICMP) { + uint16_t ip_hl; + struct IcmpHeader *icmp; + ip_hl = (p->ip->ip_vhl & 0x0f) << 2; + void *nexthdr; + + icmp = (struct IcmpHeader *)((char *)p->ip + ip_hl); + nexthdr = (void *)icmp; + if (icmp->icmp_type == ICMP_TIMXCEED) { + struct IpHeader off_ip; + struct TcpHeader off_tcp; + + bzero(&off_ip, sizeof(struct IpHeader)); + nexthdr = nexthdr + sizeof(struct IcmpHeader); + memcpy((void *)&off_ip, nexthdr, + sizeof(struct IpHeader)); + nexthdr += sizeof(struct IpHeader); + + bzero(&off_tcp, sizeof(struct TcpHeader)); + memcpy(&off_tcp, nexthdr, 4); + if (session.src == off_ip.ip_src && + session.dst == off_ip.ip_dst) { + printf("Received ICMP TTL exceeded from %s:" + "ttl used %u ip_tos 0x%x sport %u dport %u\n", + InetAddress(p->ip->ip_src), init_ttl, off_ip.ip_tos, + ntohs(off_tcp.tcp_sport), ntohs(off_tcp.tcp_dport)); + } + } + } + /* + * otherwise, this is a bad packet + * we must quit + */ + //processBadPacket(p); + } + if ((GetTime() - startTime >= 1) && (sendflag == 0)) { + sendflag = 1; + session.snd_nxt = session.snd_una; + } + } + + FreeIPPacket(&datapkt); +} +void ECNPathCheckTest(u_int32_t sourceAddress, u_int16_t sourcePort, + u_int32_t targetAddress, u_int16_t targetPort, int mss) +{ + int rawSocket, rc, flag; + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = 5*mss; + session.snd_nxt = arc4random(); + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss; + session.maxseqseen = 0; + session.epochTime = GetTime(); + session.maxpkts = 1000; + session.debug = 0; + + if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t), + mss * session.maxpkts)) == NULL) { + printf("no memory to store data, error: %d \n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + Quit(ERR_SOCKET_OPEN); + } + + flag = 1; + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, + (char *)&flag, sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + /* Establish a TCP connections with ECN bits */ + rc = EstablishTcpConnection(TCPFLAGS_ECN_ECHO | TCPFLAGS_CWR, 0); + switch (rc) { + case ESTABLISH_FAILURE_EARLY_RST: + { + /* Received a RST when ECN bits are used. Try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Received RST with or without ECN negotiation\n"); + printf("The server might not be listening on this port\n"); + Quit(EARLY_RST); + } else if (rc == ESTABLISH_SUCCESS) { + printf("Received RST with ECN.\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Received RST with ECN\n"); + printf("Exceed max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } + break; + } + case ESTABLISH_FAILURE_NO_REPLY: + { + /* No reply after retring, try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Received RST without ECN\n"); + Quit(NO_CONNECTION); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Exceeded max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } else { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } + break; + } + } + + DataPktPathCheck(session.filename, 3, 0); + return; +} + + +void +SynTest(u_int32_t sourceAddress, u_int16_t sourcePort, + u_int32_t targetAddress, u_int16_t targetPort, int mss, int syn_reply) +{ + int rawSocket, flag; + struct IPPacket *synPacket = NULL, *ackPacket = NULL; + char *read_packet; + struct pcap_pkthdr pi; + int synAckReceived = 0; + int numRetransmits = 0; + double timeoutTime; + int tcpoptlen = 4; /* For negotiating MSS */ + u_int8_t *opt = NULL; + struct IPPacket *p = NULL; + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = 5*mss; + session.snd_nxt = arc4random(); + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss; + session.maxseqseen = 0; + session.epochTime = GetTime(); + session.maxpkts = 1000; + + if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t), + mss * session.maxpkts)) == NULL) { + printf("no memory to store data, error: %d \n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + Quit(ERR_SOCKET_OPEN); + } + + flag = 1; + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, + (char *)&flag, sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + + /* allocate the syn packet -- Changed for new IPPacket structure */ + synPacket = AllocateIPPacket(0, tcpoptlen, 0, "ECN (SYN)"); + opt = (((u_int8_t *)synPacket->tcp) + sizeof(struct TcpHeader)); + opt[0] = (u_int8_t)TCPOPT_MAXSEG; + opt[1] = (u_int8_t)TCPOLEN_MAXSEG; + *((u_int16_t *)((u_int8_t *)opt + 2)) = htons(session.mss); + + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN , 0, tcpoptlen, 0); + timeoutTime = GetTime() + 1; + + /* + * Wait for SYN/ACK and retransmit SYN if appropriate + * not great, but it gets the job done + */ + + while(!synAckReceived && numRetransmits < 3) { + while(GetTime() < timeoutTime) { + /* Have we captured any packets? */ + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + /* Received a packet from us to them */ + if (INSESSION(p, session.src, session.sport, + session.dst, session.dport)) { + /* Is it a SYN/ACK? */ + if (p->tcp->tcp_flags & TCPFLAGS_SYN) { + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + session.totSeenSent++ ; + } else { + processBadPacket(p); + } + continue; + } + + /* Received a packet from them to us */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport)) { + /* Is it a SYN/ACK? */ + if ((p->tcp->tcp_flags & TCPFLAGS_SYN) && + (p->tcp->tcp_flags & TCPFLAGS_ACK)) { + timeoutTime = GetTime(); /* force exit */ + synAckReceived++; + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + + /* + * Save ttl for,admittedly poor,indications of reverse + * route change + */ + session.ttl = p->ip->ip_ttl; + session.snd_wnd = ntohl(p->tcp->tcp_win); + session.totRcvd ++; + break; + } else { + if ((p->tcp->tcp_flags)& (TCPFLAGS_RST)) { + printf ("ERROR: EARLY_RST\n"); + goto done; + } + } + } + } + } + + if (!synAckReceived) { + if (session.debug >= SESSION_DEBUG_LOW) { + printf("SYN timeout. Retransmitting\n"); + } + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN , 0, tcpoptlen, 0); + timeoutTime = GetTime() + 1; + numRetransmits++; + } + } + + if (numRetransmits >= 3) { + printf("ERROR: No connection after 3 retries...\nRETURN CODE: %d\n", + NO_CONNECTION); + goto done; + } + if (session.debug >= SESSION_DEBUG_LOW) + printf("Received SYN-ACK\n"); + if (syn_reply != 0) { + /* Update session variables */ + session.irs = ntohl(p->tcp->tcp_seq); + session.dataRcvd[0] = 1 ; + session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */ + session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */ + session.snd_una = session.iss + 1; + session.maxseqseen = ntohl(p->tcp->tcp_seq); + session.initSession = 1; + if (session.debug >= SESSION_DEBUG_LOW) { + printf("try to send the %s\n", syn_reply == TCPFLAGS_ACK ? "third Ack" : "RST"); + printf("src = %s:%d (%u)\n", InetAddress(session.src), + session.sport, session.iss); + printf("dst = %s:%d (%u)\n",InetAddress(session.dst), + session.dport, session.irs); + } + + /* allocate the syn packet -- Changed for new IPPacket structure */ + ackPacket = AllocateIPPacket(0, 0, 0, "SYN reply"); + /* send an ACK */ + SendSessionPacket(ackPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + syn_reply, 0, 0, 0); + FreeIPPacket(&ackPacket); + } +done: + FreeIPPacket(&synPacket); +} diff --git a/network_cmds/ecnprobe/ecn.h b/network_cmds/ecnprobe/ecn.h new file mode 100644 index 0000000..5ac582c --- /dev/null +++ b/network_cmds/ecnprobe/ecn.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +void ECNTest (u_int32_t sourceIpAddress, u_int16_t sourcePort, u_int32_t targetIpAddress, u_int16_t targetPort, int mss) ; +void ECNAckData (struct IPPacket *p); +void DataPkt (char *filename, u_int8_t iptos, u_int8_t tcp_flags); +void checkECN (); +void ECNPathCheckTest(u_int32_t sourceIpAddress, u_int16_t surcePort, + u_int32_t targetIpAddress, u_int16_t targetPort, int mss); +void SynTest(u_int32_t sourceIpAddress, u_int16_t surcePort, u_int32_t targetIpAddress, u_int16_t targetPort, int mss, int syn_reply); diff --git a/network_cmds/ecnprobe/ecn_probe.c b/network_cmds/ecnprobe/ecn_probe.c new file mode 100644 index 0000000..362ff4f --- /dev/null +++ b/network_cmds/ecnprobe/ecn_probe.c @@ -0,0 +1,469 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <netdb.h> +#include <fcntl.h> +#include <arpa/inet.h> +#include <spawn.h> +#include <ifaddrs.h> +#include "inet.h" +#include "capture.h" +#include "support.h" +#include "session.h" +#include "ecn.h" +#include "history.h" + +extern struct TcpSession session; + +void usage (char *name); +int GetCannonicalInfo(char *string, size_t str_size, u_int32_t *address); +int BindTcpPort(int sockfd) ; + +void usage(char *name) +{ + printf("%s [options]\n", name); + printf("\t-n <target hostname | ipaddress>\n"); + printf("\t-p <target port>\n"); + printf("\t-m <mss>\n"); + printf("\t-M <mtu>\n"); + printf("\t-w <sourcePort>\n"); + printf("\t-s <source hostname or ip address>\n"); + printf("\t-f <file-name to get>\n"); + printf("\t-d <interface name>\n"); + printf("\t-C for CE path check\n"); + printf("\t-S [A|R|X] SYN followed by ACK or RST or nothing\n"); + printf("\t-F [set|clear|skip] how to handle firewall rules\n"); + return; +} + +void SetupFirewall(u_int32_t targetIP, u_int16_t port, char *dev) +{ + char pfcmd[512]; + char *pf_file_name = "/tmp/pf.conf"; + int pf_fd = 0, rc; + ssize_t bytes; + char *args[4]; + + bzero(pfcmd, sizeof(pfcmd)); + + bzero(args, sizeof(args)); + sprintf(pfcmd, "block in quick on %s inet proto tcp from %s port %u\n", + dev, InetAddress(targetIP), port); + if (session.debug >= SESSION_DEBUG_LOW) + printf("PF rule: %s\n", pfcmd); + + pf_fd = open(pf_file_name, O_RDWR|O_TRUNC|O_CREAT); + if (pf_fd < 0) { + perror("failed to open pf file"); + exit(1); + } + bytes = write(pf_fd, pfcmd, strlen(pfcmd) + 1); + close(pf_fd); + args[0] = "pfctl"; + args[1] = "-d"; + args[2] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -d: %d\n", rc); + Quit(FAIL); + } + + args[1] = "-f"; + args[2] = pf_file_name; + args[3] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -f /tmp/pf.conf: %d\n", rc); + Quit(FAIL); + } + + args[1] = "-e"; + args[2] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -e: %d\n", rc); + Quit(FAIL); + } +} + +void CleanupFirewall() +{ + char * args[3]; + int rc; + + args[0] = "pfctl"; + args[1] = "-d"; + args[2] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -d: %d\n", rc); + Quit(FAIL); + } +} + +void Cleanup() +{ + if (session.initSession > 0) { + shutdown(session.socket, 2); + } + if (session.initCapture > 0) { + CaptureEnd(); + } + if (session.initFirewall > 0) { + CleanupFirewall(); + } +} + +void Quit(int how) +{ + SendReset(); + Cleanup(); + fflush(stdout); + fflush(stderr); + exit(how); +} + +void SigHandle(int signo) +{ + Cleanup(); + fflush(stdout); + fflush(stderr); + exit(-1); +} + +int GetCannonicalInfo(char *string, size_t str_size, u_int32_t *address) +{ + struct hostent *hp; + /* Is string in dotted decimal format? */ + if ((*address = inet_addr(string)) == INADDR_NONE) { + /* No, then lookup IP address */ + if ((hp = gethostbyname(string)) == NULL) { + /* Can't find IP address */ + printf("ERROR: Couldn't obtain address for %s\n" + "RETURN CODE: %d\n", string, FAIL); + return(-1); + } else { + strlcpy(string, hp->h_name, str_size); + memcpy((void *)address, (void *)hp->h_addr, + hp->h_length); + } + } else { + if ((hp = gethostbyaddr((char *)address, sizeof(*address), + AF_INET)) == NULL) { + /* + * Can't get cannonical hostname, so just use + * input string + */ + if (session.debug) { + printf("WARNING: Couldn't obtain cannonical" + " name for %s\nRETURN CODE: %d", + string, NO_SRC_CANON_INFO); + } + /* strlcpy(name, string, MAXHOSTNAMELEN);*/ + } else { + /* strlcpy(name, hp->h_name, MAXHOSTNAMELEN);*/ + } + } + return(0); +} + +int BindTcpPort(int sockfd) +{ + struct sockaddr_in sockName; + int port, result; + int randomOffset; + +#define START_PORT (50*1024) +#define END_PORT (0xFFFF) + + /* + * Choose random offset to reduce likelihood of + * collision with last run + */ + randomOffset = (int)(1000.0*drand48()); + + /* Try to find a free port in the range START_PORT+1..END_PORT */ + port = START_PORT+randomOffset; + do { + ++port; + sockName.sin_addr.s_addr = INADDR_ANY; + sockName.sin_family = AF_INET; + sockName.sin_port = 0; //htons(port); + result = bind(sockfd, (struct sockaddr *)&sockName, + sizeof(sockName)); + } while ((result < 0) && (port < END_PORT)); + + + if (result < 0) { + /* No free ports */ + perror("bind"); + port = 0; + } else { + socklen_t len = sizeof(sockName); + result = getsockname(sockfd, (struct sockaddr *)&sockName, &len); + if (result < 0) { + perror("getsockname"); + port = 0; + } else { + port = ntohs(sockName.sin_port); + } + } + return port; + +} + +#define FIREWALL_DEFAULT 0 +#define FIREWALL_SET_ONLY 1 +#define FIREWALL_CLEAR_ONLY 2 +#define FIREWALL_SKIP 3 + +int main(int argc, char **argv) +{ + u_int32_t targetIpAddress = 0; + u_int16_t targetPort = DEFAULT_TARGETPORT; + u_int16_t sourcePort = 0; + u_int32_t sourceIpAddress = 0; + int mss = DEFAULT_MSS; + int mtu = DEFAULT_MTU; + int fd, opt, usedev = 0, rc = 0, path_check = 0; + int syn_test = 0, syn_reply = 0; + struct sockaddr_in saddr; + char dev[11]; /* device name for pcap init */ + struct ifaddrs *ifap, *tmp; + int firewall_mode = FIREWALL_DEFAULT; + + bzero(&session, sizeof(session)); + while ((opt = getopt(argc, argv, "n:p:w:m:M:s:d:f:-CS:vF:")) != -1) { + switch (opt) { + case 'n': + if (strlen(optarg) > (MAXHOSTNAMELEN - 1)) { + printf("Target host name too long, max %u chars\n", MAXHOSTNAMELEN); + Quit(FAIL); + } + strlcpy(session.targetHostName, optarg, + sizeof(session.targetHostName)); + strlcpy(session.targetName, session.targetHostName, + sizeof(session.targetName)); + break; + case 'p': + targetPort = atoi(optarg); + break; + case 'm': + mss = atoi(optarg); + break; + case 'M': + mtu = atoi(optarg); + break; + case 'w': + sourcePort = atoi(optarg); + break; + case 's': + if (strlen(optarg) > (MAXHOSTNAMELEN - 1)) { + printf("Source host name too long, max %u chars\n", MAXHOSTNAMELEN); + Quit(FAIL); + } + strlcpy(session.sourceHostName, optarg, + MAXHOSTNAMELEN); + break; + case 'd': + if (strlen(optarg) > (sizeof(dev) - 1)) { + printf("Interface nae is too large, max %lu chars\n", (sizeof(dev) - 1)); + Quit(FAIL); + } + bzero(dev, sizeof(dev)); + strlcpy(dev, optarg, sizeof(dev)); + usedev = 1; + break; + case 'f': + if (strlen(optarg) > 0) { + session.filename = strndup(optarg, strlen(optarg) + 1); + } else { + printf("Invalid file name \n"); + } + break; + case 'F': + if (strcasecmp(optarg, "default") == 0) + firewall_mode = FIREWALL_DEFAULT; + else if (strcasecmp(optarg, "set") == 0) + firewall_mode = FIREWALL_SET_ONLY; + else if (strcasecmp(optarg, "clear") == 0) + firewall_mode = FIREWALL_CLEAR_ONLY; + else if (strcasecmp(optarg, "skip") == 0) + firewall_mode = FIREWALL_SKIP; + else + printf("firewall mode\n"); + break; + case 'C': + path_check = 1; + break; + case 'S': + syn_test = 1; + if (strcasecmp(optarg, "A") == 0) + syn_reply = TCPFLAGS_ACK; + else if (strcasecmp(optarg, "R") == 0) + syn_reply = TCPFLAGS_RST; + else if (strcasecmp(optarg, "X") == 0) + syn_reply = 0; + else + printf("Invalid SYN reply \n"); + break; + case 'v': + session.debug++; + break; + default: + usage(argv[0]); + exit(1); + } + } + signal(SIGTERM, SigHandle); + signal(SIGINT, SigHandle); + signal(SIGHUP, SigHandle); + + if (GetCannonicalInfo(session.targetHostName, sizeof(session.targetHostName), + &targetIpAddress) < 0) + { + printf("Failed to convert targetIP address\n"); + Quit(NO_TARGET_CANON_INFO); + } + rc = getifaddrs(&ifap); + if (rc != 0 || ifap == NULL) { + printf("Failed to get source addresswith getifaddrs: %d\n", rc); + Quit(FAIL); + } + tmp = ifap; + sourceIpAddress = 0; + bzero(session.sourceHostName, MAXHOSTNAMELEN); + for (tmp = ifap; tmp != NULL; tmp = tmp->ifa_next) { + struct sockaddr_in *sin; + if (tmp->ifa_addr == NULL) + continue; + if (tmp->ifa_addr->sa_family != PF_INET) + continue; + if (usedev == 1) { + /* we know which interface to use */ + if (strcmp(dev, tmp->ifa_name) == 0) { + sin = (struct sockaddr_in *)tmp->ifa_addr; + sourceIpAddress = sin->sin_addr.s_addr; + strlcpy(session.sourceHostName, + inet_ntoa(sin->sin_addr), + sizeof(session.sourceHostName)); + } else { + continue; + } + } else { + /* pick the first address */ + bzero(dev, sizeof(dev)); + sin = (struct sockaddr_in *)tmp->ifa_addr; + sourceIpAddress = sin->sin_addr.s_addr; + strlcpy(session.sourceHostName, + inet_ntoa(sin->sin_addr), + sizeof(session.sourceHostName)); + strlcpy(dev, tmp->ifa_name, sizeof(dev)); + } + } + freeifaddrs(ifap); + if (sourceIpAddress == 0) { + printf("Failed to get source Ip address\n"); + Quit(FAIL); + } + + if (sourcePort == 0) { + bzero(&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("Can't open socket\n"); + return (-1); + } + if ((sourcePort = BindTcpPort(fd)) == 0) { + printf("Can't bind to port\n"); + return (-1); + } + } + printf("Source: %s:%d\n", session.sourceHostName, sourcePort); + printf("Destination: %s:%d\n", session.targetHostName, targetPort); + + switch (firewall_mode) { + case FIREWALL_DEFAULT: + SetupFirewall(targetIpAddress, targetPort, dev); + session.initFirewall = 1; + break; + case FIREWALL_SET_ONLY: + SetupFirewall(targetIpAddress, targetPort, dev); + goto done; + case FIREWALL_CLEAR_ONLY: + session.initFirewall = 1; + goto done; + case FIREWALL_SKIP: + break; + } + + CaptureInit(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, dev); + session.initCapture = 1; + + + printf("Starting ECN test\n"); + if (syn_test) { + session.dont_send_reset = 1; + SynTest(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, mss, syn_reply); + } else if (path_check) { + ECNPathCheckTest(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, mss); + } else { + ECNTest(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, mss); + } +done: + Quit(SUCCESS); + close(session.socket); + return (0); +} diff --git a/network_cmds/ecnprobe/ecnprobe.1 b/network_cmds/ecnprobe/ecnprobe.1 new file mode 100644 index 0000000..76c2173 --- /dev/null +++ b/network_cmds/ecnprobe/ecnprobe.1 @@ -0,0 +1,20 @@ +.Dd 5/7/2015 +.Dt ecnprobe 1 +.Os Darwin +.Sh NAME +.Nm ecnprobe +.Nd Tool to probe for TCP ECN related incompatibilities in the network +.Sh SYNOPSIS +.Nm +.Op Fl n Ar target hostname or ipaddress +.Op Fl p Ar target port +.Op Fl m Ar mss +.Op Fl M Ar mtu +.Op Fl w Ar source port +.Op Fl s Ar source ip address +.Op Fl f Ar file name to get +.Op Fl d Ar interface name +.Sh DESCRIPTION +The +.Nm +utility can be used to test TCP ECN related incompatibilities in the network. diff --git a/network_cmds/ecnprobe/gmt2local.c b/network_cmds/ecnprobe/gmt2local.c new file mode 100644 index 0000000..a6114f3 --- /dev/null +++ b/network_cmds/ecnprobe/gmt2local.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <sys/types.h> +#include <sys/time.h> + +#include <stdio.h> +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif + +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "gmt2local.h" + +/* + * Returns the difference between gmt and local time in seconds. + * Use gmtime() and localtime() to keep things simple. + */ +int32_t +gmt2local(time_t t) +{ + register int dt, dir; + register struct tm *gmt, *loc; + struct tm sgmt; + + if (t == 0) + t = time(NULL); + gmt = &sgmt; + *gmt = *gmtime(&t); + loc = localtime(&t); + dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + + (loc->tm_min - gmt->tm_min) * 60; + + /* + * If the year or julian day is different, we span 00:00 GMT + * and must add or subtract a day. Check the year first to + * avoid problems when the julian day wraps. + */ + dir = loc->tm_year - gmt->tm_year; + if (dir == 0) + dir = loc->tm_yday - gmt->tm_yday; + dt += dir * 24 * 60 * 60; + + return (dt); +} diff --git a/network_cmds/ecnprobe/gmt2local.h b/network_cmds/ecnprobe/gmt2local.h new file mode 100644 index 0000000..4933b09 --- /dev/null +++ b/network_cmds/ecnprobe/gmt2local.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: gmt2local.h,v 1.2 97/01/23 22:31:40 leres Exp $ (LBL) + */ +#ifndef gmt2local_h +#define gmt2local_h + +int32_t gmt2local(time_t); +#endif diff --git a/network_cmds/ecnprobe/history.c b/network_cmds/ecnprobe/history.c new file mode 100644 index 0000000..e19b0ba --- /dev/null +++ b/network_cmds/ecnprobe/history.c @@ -0,0 +1,211 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include "base.h" +#include "inet.h" +#include "session.h" +#include "support.h" +#include "history.h" + +extern struct TcpSession session; +struct History history[MAXHSZ]; + +void StorePacket (struct IPPacket *p) { + + uint32 src, dst, seq, ack ; + uint16 sport, dport, win, urp, datalen, optlen; + uint16 ip_optlen; + uint8 flags; + + ReadIPPacket(p, + &src, &dst, + &sport, &dport, + &seq, + &ack, + &flags, + &win, + &urp, + &datalen, + &ip_optlen, + &optlen); + + if (src == session.src) { + history[session.hsz].type = SENT; + } else { + history[session.hsz].type = RCVD ; + } + + history[session.hsz].timestamp = GetTime () - session.epochTime; + history[session.hsz].seqno = seq; + history[session.hsz].nextbyte = seq + datalen; + history[session.hsz].ackno = ack ; + history[session.hsz].fin = (flags & TCPFLAGS_FIN) ? 1 : 0; + history[session.hsz].syn = (flags & TCPFLAGS_SYN) ? 1 : 0; + history[session.hsz].rst = (flags & TCPFLAGS_RST) ? 1 : 0; + history[session.hsz].psh = (flags & TCPFLAGS_PSH) ? 1 : 0; + history[session.hsz].ack = (flags & TCPFLAGS_ACK) ? 1 : 0; + history[session.hsz].urg = (flags & TCPFLAGS_URG) ? 1 : 0; + history[session.hsz].ecn_echo = (flags & TCPFLAGS_ECN_ECHO) ? 1:0; + history[session.hsz].cwr = (flags & TCPFLAGS_CWR) ? 1 : 0; + history[session.hsz].ip_optlen = ip_optlen; + history[session.hsz].optlen = optlen ; + + /* Grab IP Options from Ip Header - New */ + if (ip_optlen > 0) { + if ((history[session.hsz].ip_opt = calloc(sizeof(uint8), ip_optlen)) == NULL) { + printf("StorePacket Error: Could not allocate history memory\nRETURN CODE: %d\n", ERR_MEM_ALLOC); + Quit (ERR_MEM_ALLOC); + } + memcpy(history[session.hsz].ip_opt, (char *)p->ip + sizeof(struct IpHeader), ip_optlen); + } + + + /* Grab TCP options from TCP Header */ + if (optlen > 0) { + if ((history[session.hsz].opt = calloc(sizeof(uint8), optlen)) == NULL) { + Quit (ERR_MEM_ALLOC); + } + + memcpy(history[session.hsz].opt, (char *)p->tcp + sizeof(struct TcpHeader), optlen); + } + + history[session.hsz].dlen = datalen; + + if ((history[session.hsz].data = calloc(sizeof(uint8), datalen)) == NULL) { + Quit (ERR_MEM_ALLOC); + } + + /* Copy data bytes */ + memcpy(history[session.hsz].data, + (char *)p->tcp + sizeof(struct TcpHeader) + optlen, + datalen); + + session.hsz++; + + if (session.hsz >= MAXHSZ) { + Quit(TOO_MANY_PKTS); + } + +} + +int reordered(struct IPPacket *p) { + + int i; + int count = 0; + double ts = -99999; + + /* + * This might be either an unwanted packet drop, or just a reordering. + * Test: + * If we have not sent three acks for this packet + * AND the gap between this packet and previous one is "small" (i.e. not a timeout) + * then its a reordering, and not a retransmission. + */ + + /* count the number of (dup) ACKs sent */ + for (i = 0; i < session.hsz; i++) { + if ((history[i].type == SENT) && + (history[i].ack)) { + if (history[i].ackno == history[session.hsz - 1].seqno) + count += 1; + } + } + + if (count > 0) { + + session.num_dup_acks += count - 1; + + switch (count) { + case 1: /* no dup acks */ + session.num_pkts_0_dup_acks += 1; + break; + + case 2: /* 1 dup acks */ + session.num_pkts_1_dup_acks += 1; + break; + + case 3: /* 2 dup acks */ + session.num_pkts_2_dup_acks += 1; + break; + + case 4: /* 3 dup acks */ + session.num_pkts_3_dup_acks += 1; + break; + + default: + session.num_pkts_4_or_more_dup_acks += 1; + break; + } + } + + /* 3 dup acks? => Fast retransmission */ + if (count > 3) { + printf("Fast retransmit...\n"); + return 3; + } + + /* Compute elapsed time between this packet and the previously RCVD packet */ + for (i = (session.hsz - 2); i >= 0; i--) { + if ((history[i].type == RCVD) && (history[i].dlen > 0)) { + ts = history[i].timestamp; + break; + } + } + + if ((history[session.hsz - 1].timestamp - ts) > RTT_TO_MULT * (session.rtt + PLOTDIFF)) { + printf ("RTO ===> %f %f\n", history[session.hsz - 1].timestamp, ts); + return 2; + } + + printf ("#### Acks %d\n", count); + printf ("#### reordering detected\n"); + session.num_reordered++; + + return 1; + +} diff --git a/network_cmds/ecnprobe/history.h b/network_cmds/ecnprobe/history.h new file mode 100644 index 0000000..9e69d44 --- /dev/null +++ b/network_cmds/ecnprobe/history.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#define SENT 1 +#define RCVD 2 +#define MAXHSZ 10000 + +struct History { /* store history of each packet as it is seen */ + + int type ; /* sent or received */ + double timestamp; /* when */ + uint32 seqno; + uint32 nextbyte; /* seqno + dlen */ + uint32 ackno; + int hlen; + int ecn_echo; + int cwr; + int urg; + int ack; + int psh; + int rst; + int syn; + int fin; + int ip_optlen; /* added to support IP options */ + uint8 *ip_opt; /* added to support IP options */ + int optlen; + uint8 *opt; + uint8 *data; + int dlen; + + int pkt_num; + +}; + +void StorePacket (struct IPPacket *p); +int reordered (struct IPPacket *p); diff --git a/network_cmds/ecnprobe/inet.c b/network_cmds/ecnprobe/inet.c new file mode 100644 index 0000000..b145412 --- /dev/null +++ b/network_cmds/ecnprobe/inet.c @@ -0,0 +1,503 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" +#include "history.h" + +extern struct TcpSession session; +extern struct History history[]; + +/* + * Deal with struct in_addr type agreement once and for all + */ +char *InetAddress(uint32 addr) +{ + + struct in_addr s; + s.s_addr = addr; + + //printf("In InetAddress:\n"); + //printf("addr = %s (%0x)\n", inet_ntoa(s), addr); + + return (inet_ntoa(s)); +} + +/* + * Really slow implementation of ip checksum + * ripped off from rfc1071 + */ + +uint16 InetChecksum(uint16 *ip, uint16 *tcp, uint16 ip_len, uint16 tcp_len) { + + uint32 sum = 0; + + uint32 ip_count = ip_len; + uint32 tcp_count = tcp_len; + uint16 *ip_addr = ip; + uint16 *tcp_addr = tcp; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In InetChecksum...\n"); + printf("iplen: %d, tcplen: %d\n", ip_len, tcp_len); + } + + + while(ip_count > 1) { + //printf("ip[%d]: %x\n", ip_len - ip_count, htons(*ip_addr)); + sum += *ip_addr++; + ip_count -= 2; + } + + while(tcp_count > 1) { + //printf("tcp[%d]: %x\n", tcp_len - tcp_count, htons(*tcp_addr)); + sum += *tcp_addr++; + tcp_count -= 2; + } + + if(ip_count > 0) { + sum += *(uint8 *)ip_addr; + } + + if(tcp_count > 0) { + sum += *(uint8 *)tcp_addr; + } + + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out InetChecksum...\n"); + } + + return(~sum); + +} + + +void WriteIPPacket(struct IPPacket *p, + uint32 src, + uint32 dst, + uint16 sport, + uint16 dport, + uint32 seq, + uint32 ack, + uint8 flags, + uint16 win, + uint16 urp, + uint16 datalen, + uint16 ip_optlen, + uint16 optlen, + uint8 iptos, + uint8 u4tf) +{ + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In WriteIPPacket...\n"); + } + + /* Zero out IpHeader to ensure proper checksum computation */ + bzero((char *)(p->ip), sizeof(struct IpHeader)); + + ip->ip_src = src; + ip->ip_dst = dst; + ip->ip_p = IPPROTOCOL_TCP; + ip->ip_xsum = + htons((uint16)(sizeof(struct TcpHeader) + datalen + optlen)); /* pseudo hdr */ + + tcp->tcp_sport = htons(sport); + tcp->tcp_dport = htons(dport); + tcp->tcp_seq = htonl(seq); + tcp->tcp_ack = htonl(ack); + tcp->tcp_hl = (sizeof(struct TcpHeader) + optlen) << 2; + tcp->tcp_hl = tcp->tcp_hl | u4tf; + tcp->tcp_flags = flags; + + tcp->tcp_win = htons(win); + tcp->tcp_urp = htons(urp); + + tcp->tcp_xsum = 0; + tcp->tcp_xsum = InetChecksum((uint16 *)ip, (uint16 *)tcp, + (uint16)sizeof(struct IpHeader), /* IP Options should aren't included */ + (uint16)(sizeof(struct TcpHeader) + datalen + optlen)); + + /* Fill in real ip header */ + if (session.curr_ttl != 0) { + ip->ip_ttl = session.curr_ttl; + }else { + ip->ip_ttl = 60; + } + + //printf("TTL: %d\n", ip->ip_ttl); + + ip->ip_tos = iptos; + + /* IP Version and Header len field */ + ip->ip_vhl = 0x40 + 0x5 + (int)(ip_optlen/4); + ip->ip_p = IPPROTOCOL_TCP; + + ip->ip_off = IP_DF; + ip->ip_len = (uint16)(sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen + datalen); + + ip->ip_xsum = 0; + ip->ip_xsum = InetChecksum((uint16 *)ip, NULL, + (uint16)sizeof(struct IpHeader) + ip_optlen, /* IP Options should aren't included */ + 0); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out WriteIPPacket...\n"); + } + +} + +void ReadIPPacket(struct IPPacket *p, + uint32 *src, + uint32 *dst, + uint16 *sport, + uint16 *dport, + uint32 *seq, + uint32 *ack, + uint8 *flags, + uint16 *win, + uint16 *urp, + uint16 *datalen, + uint16 *ip_optlen, + uint16 *optlen) +{ + + /* TODO: Add reading of IP options, if any */ + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + uint16 ip_len; + uint16 ip_hl; + uint16 tcp_hl; + + /* XXX do checksum check? */ + if (ip->ip_p != IPPROTOCOL_TCP && ip->ip_p != IPPROTOCOL_ICMP) { + printf("Unexpected protocol packet: %u\n", ip->ip_p); + Quit(ERR_CHECKSUM); + } + + *src = ip->ip_src; + *dst = ip->ip_dst; + *sport = ntohs(tcp->tcp_sport); + *dport = ntohs(tcp->tcp_dport); + *seq = ntohl(tcp->tcp_seq); + *ack = ntohl(tcp->tcp_ack); + *flags = tcp->tcp_flags; + *win = ntohs(tcp->tcp_win); + *urp = ntohs(tcp->tcp_urp); + + tcp_hl = tcp->tcp_hl >> 2; + ip_len = ntohs(ip->ip_len); + ip_hl = (ip->ip_vhl & 0x0f) << 2; + *datalen = (ip_len - ip_hl) - tcp_hl; + *ip_optlen = ip_hl - (unsigned int)sizeof(struct IpHeader); /* added to support IP Options */ + *optlen = tcp_hl - (unsigned int)sizeof(struct TcpHeader); + +} + +void PrintICMPUnreachableErrorPacket(struct ICMPUnreachableErrorPacket *p) +{ + + struct IpHeader *ip = &p->ip; + struct IcmpHeader *icmp = &p->icmp; + struct IpHeader *off_ip = &p->off_ip; + + printf("IPHdr: "); + printf("%s > ", InetAddress(ip->ip_src)); + printf("%s ", InetAddress(ip->ip_dst)); + printf(" datalen: %u\n", ip->ip_len); + printf("ICMPHdr: "); + printf("Type: %u Code: %u MTU next hop: %u xsum: %x\n", + icmp->icmp_type, + icmp->icmp_code, + ntohs(icmp->icmp_mtu), + icmp->icmp_xsum); + printf("Off IPHdr: "); + printf("%s > ", InetAddress(off_ip->ip_src)); + printf("%s ", InetAddress(off_ip->ip_dst)); + printf(" datalen: %u ", off_ip->ip_len); + printf("tcp sport: %u ", ntohs(p->tcp_sport)); + printf("tcp dport: %u ", ntohs(p->tcp_dport)); + printf("tcp seqno: %u\n", (uint32)ntohl(p->tcp_seqno)); + +} + +void PrintTcpPacket(struct IPPacket *p) +{ + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + char *opt; + int optlen; + char *ip_opt; + int ip_optlen; + int i; + + printf("%s.%u > ", InetAddress(ip->ip_src), ntohs(tcp->tcp_sport)); + printf("%s.%u ", InetAddress(ip->ip_dst), ntohs(tcp->tcp_dport)); + + if (tcp->tcp_flags & TCPFLAGS_SYN) { + printf("S"); + } + + if (tcp->tcp_flags & TCPFLAGS_ACK) { + printf("A"); + } + + if (tcp->tcp_flags & TCPFLAGS_FIN) { + printf("F"); + } + + if (tcp->tcp_flags & TCPFLAGS_ECN_ECHO) { + printf("E"); + } + + if (tcp->tcp_flags & TCPFLAGS_CWR) { + printf("W"); + } + + if (tcp->tcp_flags & TCPFLAGS_RST) { + printf("R"); + } + if (tcp->tcp_flags & TCPFLAGS_PSH) { + printf("P"); + } + + if (tcp->tcp_flags & TCPFLAGS_URG) { + printf("U"); + } + + if (INSESSION(p,session.src,session.sport,session.dst,session.dport)) { + printf(" seq: %u, ack: %u", (uint32)ntohl(tcp->tcp_seq) - session.iss, (uint32)ntohl(tcp->tcp_ack) - session.irs); + } else { + printf(" seq: %u, ack: %u", (uint32)ntohl(tcp->tcp_seq) - session.irs, (uint32)ntohl(tcp->tcp_ack) - session.iss); + } + + /* IP Options */ + ip_optlen = ((ip->ip_vhl & 0x0f) << 2) - sizeof(struct IpHeader); + ip_opt = (char *)ip + sizeof(struct IpHeader); + + i = 0; + while (i < ip_optlen) { + + switch ((unsigned char)ip_opt[i]) { + case IPOPT_NOP: + printf(" ipopt%d: %s ", i + 1, "IPOPT_NOP"); + i = i + 1; + break; + + case IPOPT_EOL: + printf(" ipopt%d: %s ", i + 1, "IPOPT_EOL"); + i = ip_optlen + 1; + break; + + case IPOPT_RR: + printf(" ipopt%d: %s ", i + 1, "IPOPT_RR"); + i = i + IPOLEN_RR; + break; + + default: + printf("ip_opt%d: UNKNOWN ", i + 1); + i = i + (uint8)ip_opt[i+1] ; + } + } + + printf(" win: %u, urg: %u, ttl: %d", ntohs(tcp->tcp_win), ntohs(tcp->tcp_urp), ip->ip_ttl); + printf(" datalen: %u, optlen: %u ", + ip->ip_len - ((ip->ip_vhl &0x0f) << 2) - (tcp->tcp_hl >> 2), + (tcp->tcp_hl >> 2) - (unsigned int)sizeof(struct TcpHeader)); + + + /* TCP Options */ + optlen = (tcp->tcp_hl >> 2) - (unsigned int)sizeof (struct TcpHeader) ; + opt = (char *)tcp + sizeof(struct TcpHeader); + + i = 0 ; + + while (i < optlen) { + + switch ((unsigned char)opt[i]) { + + case TCPOPT_EOL: + printf (" opt%d: %s ", i + 1, "TCPOPT_EOL"); + i = optlen + 1; + break ; + + case TCPOPT_NOP: + printf (" opt%d: %s ", i + 1, "TCPOPT_NOP"); + i++ ; + break ; + + case TCPOPT_MAXSEG: + printf (" opt%d: %s: %d ", i + 1, "TCPOPT_MAXSEG", ntohs(*(uint16 *)((char *)opt+2))); + i = i + TCPOLEN_MAXSEG ; + break ; + + case TCPOPT_WINDOW: + printf (" opt%d: %s ", i + 1, "TCPOPT_WINDOW"); + i = i + TCPOLEN_WINDOW ; + break ; + + case TCPOPT_SACK_PERMITTED: + printf (" opt%d: %s ", i + 1, "TCPOPT_SACK_PERMITTED"); + i = i + TCPOLEN_SACK_PERMITTED ; + break ; + + case TCPOPT_TIMESTAMP: + printf (" opt%d: %s ", i + 1, "TCPOPT_TIMESTAMP"); + i = i + TCPOLEN_TIMESTAMP ; + break ; + + default: + printf (" opt%d c:%d l:%d: UNKNOWN ", i + 1, (uint8)opt[i], (uint8)opt[i+1]); + if ((uint8)opt[i+1] > 0) { + i = i + (uint8)opt[i+1] ; + } else { + Quit(20); + } + break ; + } + } + printf ("\n"); +} + + +struct IPPacket *FindHeaderBoundaries(char *p) { + + struct IPPacket *packet; + uint16 ip_hl; + + if ((packet = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) == NULL) { + printf("FindHeaderBoundaries: Cannot allocate memory for read packet\nRETURN CODE: %d\n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + packet->ip = (struct IpHeader *)p; + + if (packet->ip->ip_p != IPPROTOCOL_TCP && + packet->ip->ip_p != IPPROTOCOL_ICMP) { + printf("Error: Unexpected protocol packet: %u \n", packet->ip->ip_p); + Quit(ERR_CHECKSUM); + } + + ip_hl = (packet->ip->ip_vhl & 0x0f) << 2; + + packet->tcp = (struct TcpHeader *)((char *)p + ip_hl); + return packet; + +} + + +struct IPPacket * +AllocateIPPacket(int ip_optlen, int tcp_optlen, int datalen, char *str) +{ + struct IPPacket *p; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In AllocateIPPacket: %s...\n", str); + } + + if ((p = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) + == NULL) { + printf("%s ERROR: No space for packet\nRETURN CODE: %d", + str, ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((p->ip = (struct IpHeader *)calloc(1, + sizeof(struct IpHeader) + ip_optlen)) == NULL) { + printf("%s ERROR: No IpHeader space for packet\n" + "RETURN CODE: %d", str, ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((p->tcp = (struct TcpHeader *)calloc(1, + sizeof(struct TcpHeader) + tcp_optlen + datalen)) == NULL) { + printf("%s ERROR: No TcpHeader space for packet\n" + "RETURN CODE: %d", str, ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of AllocateIPPacket: %s...\n", str); + } + return(p); +} + +void +FreeIPPacket(struct IPPacket **pkt_p) +{ + struct IPPacket *pkt; + if (pkt_p == NULL) + return; + if ((pkt = *pkt_p) == NULL) + return; + if (pkt->ip != NULL) { + free(pkt->ip); + pkt->ip = NULL; + } + if (pkt->tcp != NULL) { + free(pkt->tcp); + pkt->tcp = NULL; + } + free(pkt); + *pkt_p = NULL; +} + diff --git a/network_cmds/ecnprobe/inet.h b/network_cmds/ecnprobe/inet.h new file mode 100644 index 0000000..8d1ccff --- /dev/null +++ b/network_cmds/ecnprobe/inet.h @@ -0,0 +1,206 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#ifndef _INET_H_ +#define _INET_H_ + +/* XXX These are machine/compiler dependent */ +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +#define IPPROTOCOL_ICMP 1 +#define IPPROTOCOL_IGMP 2 +#define IPPROTOCOL_TCP 6 +#define IPPROTOCOL_UDP 17 +#define IP_DF 0x4000 + +/* ICMP type */ +#define ICMP_TIMXCEED 11 + +/* TCP Flags */ +#define TCPFLAGS_FIN 0x01 +#define TCPFLAGS_SYN 0x02 +#define TCPFLAGS_RST 0x04 +#define TCPFLAGS_PSH 0x08 +#define TCPFLAGS_ACK 0x10 +#define TCPFLAGS_URG 0x20 +#define TCPFLAGS_ECN_ECHO 0x40 +#define TCPFLAGS_CWR 0x80 + +/* IP Options Parameters -- for IP Options te*/ +#define IPOPT_EOL 0x0 +#define IPOLEN_EOL 0x1 +#define IPOPT_NOP 0x1 +#define IPOLEN_NOP 0x1 +#define IPOPT_RR 0x7 +#define IPOLEN_RR 0x27 /* Maximum length; up to 9 IP addresses */ +#define IPOPT_TS 0x44 +#define IPOLEN_TS 0x28 +#define IPOPT_FAKED 0xff +#define IPOLEN_FAKED 0x4 + +/* TCP Options Parameters */ +#define TCPOPT_EOL 0 +#define TCPOLEN_EOL 1 +#define TCPOPT_NOP 1 +#define TCPOLEN_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOLEN_MAXSEG 4 +#define TCPOPT_WINDOW 3 +#define TCPOLEN_WINDOW 3 +#define TCPOPT_SACK_PERMITTED 4 +#define TCPOLEN_SACK_PERMITTED 2 +#define TCPOPT_SACK 5 +#define TCPOPT_TIMESTAMP 8 +#define TCPOLEN_TIMESTAMP 10 +#define TCPOPT_FAKED 0x19 +#define TCPOLEN_FAKED 0x4 + +struct IpHeader { + uint8 ip_vhl; /* version (4bits) & header length (4 bits) */ + uint8 ip_tos; /* type of service */ + uint16 ip_len; /* length of IP datagram */ + uint16 ip_id; /* identification (for frags) */ + uint16 ip_off; /* offset (within a fragment) and flags (3 bits) */ + uint8 ip_ttl; /* time to live */ + uint8 ip_p; /* protocol number */ + uint16 ip_xsum; /* checksum */ + uint32 ip_src; /* source address */ + uint32 ip_dst; /* destination address */ +}; + +/* Pseudo header for doing TCP checksum calculation */ +struct PseudoIpHeader { + uint32 filler[2]; + uint8 zero; + uint8 ip_p; + uint16 ip_len; + uint32 ip_src; + uint32 ip_dst; +}; + +struct TcpHeader { + uint16 tcp_sport; /* source port */ + uint16 tcp_dport; /* destination port */ + uint32 tcp_seq; /* sequence number */ + uint32 tcp_ack; /* acknoledgement number */ + uint8 tcp_hl; /* header length (4 bits) */ + uint8 tcp_flags; /* flags */ + uint16 tcp_win; /* advertized window size */ + uint16 tcp_xsum; /* checksum */ + uint16 tcp_urp; /* urgent pointer */ +}; + + + +struct IcmpHeader { + uint8 icmp_type; /* ICMP message type */ + uint8 icmp_code; /* Message code */ + uint16 icmp_xsum; /* checksum */ + uint16 icmp_unused; /* unused field */ + uint16 icmp_mtu; /* MTU of limiting interface */ +}; + +struct IPPacket { + struct IpHeader *ip; + struct TcpHeader *tcp; +}; + +struct ICMPUnreachableErrorPacket { + struct IpHeader ip; + struct IcmpHeader icmp; + struct IpHeader off_ip; + /* 8-first bytes of TCP header */ + uint16 tcp_sport; + uint16 tcp_dport; + uint32 tcp_seqno; +}; + +struct ICMPTimeExceededErrorPacket { + struct IpHeader ip; + struct IcmpHeader icmp; + struct IpHeader off_ip; + /* 8-first bytes of Tcpheader */ + uint16 tcp_sport; + uint16 tcp_dport; + uint32 tcp_seqno; +}; + +char *InetAddress(uint32 addr); + +uint16 InetChecksum(uint16 *ip_addr, uint16 *tcp_addr, uint16 ip_len, uint16 tcp_len); + +void PrintTcpPacket(struct IPPacket *p); +void PrintICMPUnreachableErrorPacket(struct ICMPUnreachableErrorPacket *p); + +void WriteIPPacket(struct IPPacket *p, + uint32 src, + uint32 dst, + uint16 sport, + uint16 dport, + uint32 seq, + uint32 ack, + uint8 flags, + uint16 win, + uint16 urp, + uint16 datalen, + uint16 ip_optlen, + uint16 optlen, + uint8 iptos, + uint8 u4tf); + +void ReadIPPacket(struct IPPacket *p, uint32 *src, uint32 *dst, + uint16 *sport, uint16 *dport, uint32 *seq, uint32 *ack, + uint8 *flags, uint16 *win, uint16 *urp, uint16 *datalen, + uint16 *ip_optlen, uint16 *optlen); + +void StorePacket (struct IPPacket *p); + +struct IPPacket *FindHeaderBoundaries(char *p); + +struct IPPacket *AllocateIPPacket(int ip_optlen, int tcp_optlen, int datalen, char *str); + +void FreeIPPacket(struct IPPacket **pkt_p); + +#endif /* _INET_H_ */ diff --git a/network_cmds/ecnprobe/session.c b/network_cmds/ecnprobe/session.c new file mode 100644 index 0000000..5ca97d8 --- /dev/null +++ b/network_cmds/ecnprobe/session.c @@ -0,0 +1,785 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" +#include "ecn.h" +#include <errno.h> + +struct TcpSession session; + +int EstablishSession(uint32 sourceAddress, + uint16 sourcePort, + uint32 targetAddress, + uint16 targetPort, + int ip_optlen, // AM: add support for IP options + char *ip_opt, // AM: add support for IP options + int mss, + int optlen, + char *opt, + int maxwin, + int maxpkts, + uint8 iptos, + uint8 tcp_flags) // AM: Add a tcp_flags parameter +{ + + int rawSocket; + + struct IPPacket *p = NULL; + struct IPPacket *synPacket; + char *read_packet; + struct pcap_pkthdr pi; + int synAckReceived = 0; + int numRetransmits = 0; + double timeoutTime; + double ts1 = 0, ts2; + int flag = 1; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In EstablishSession...\n"); + } + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = maxwin * mss; + session.snd_nxt = arc4random(); /* random initial sequence number */ + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss ; + session.maxseqseen = 0 ; + session.epochTime = GetTime(); + session.maxpkts = maxpkts; + session.num_unwanted_drops = 0; + session.num_reordered = 0; + session.num_rtos = 0; + session.num_dup_acks = 0; + session.num_pkts_0_dup_acks = 0; + session.num_pkts_1_dup_acks = 0; + session.num_pkts_2_dup_acks = 0; + session.num_pkts_3_dup_acks = 0; + session.num_pkts_4_or_more_dup_acks = 0; + session.num_dupack_ret = 0; + session.num_reord_ret = 0; + session.num_reordered = 0; + session.num_dup_transmissions = 0; + session.ignore_result = 0; + session.curr_ttl = 0; + + if ((session.mtu < 1) || (session.mtu > 1460)) { + session.mtu = 1500; + } + + if (session.verbose) { + printf("session.MTU = %d\n", session.mtu); + } + + if ((session.dataRcvd = (uint8 *)calloc(sizeof(uint8), mss * session.maxpkts)) == NULL) { + perror("ERROR: no memmory to store data:\n"); + printf("RETURN CODE: %d\n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + /* Now open a raw socket for sending our "fake" TCP segments */ + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + printf("RETURN CODE: %d\n", ERR_SOCKET_OPEN); + Quit(ERR_SOCKET_OPEN); + } + + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, (char *)&flag,sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + printf("RETURN CODE: %d\n", ERR_SOCKOPT); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + /* Allocate SYN packet */ + synPacket = AllocateIPPacket(ip_optlen, optlen, 0, "EstablishSession (SYN)"); + + /* Copy IP options at the end of IpHeader structure - New */ + if (ip_optlen > 0) { + memcpy((char *)synPacket->ip + sizeof(struct IpHeader), ip_opt, ip_optlen); + } + + /* Copy TCP options at the end of TcpHeader structure - New */ + if (optlen > 0) { + memcpy((char *)synPacket->tcp + sizeof(struct TcpHeader), opt, optlen); + } + + /* Send SYN Pkt */ + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen, + TCPFLAGS_SYN | tcp_flags, + ip_optlen, /* IP opt len */ + optlen, /* TCP opt len */ + iptos); + + timeoutTime = GetTime() + SYNTIMEOUT; + + /* + * Wait for SYN/ACK and retransmit SYN if appropriate + * not great, but it gets the job done + */ + + while(!synAckReceived && numRetransmits < MAXSYNRETRANSMITS) { + + while(GetTime() < timeoutTime) { + + /* Have we captured any packets? */ + + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* Received a packet from us to them */ + if (INSESSION(p, session.src, session.sport, session.dst, session.dport)) { + + /* Is it a SYN? */ + if (p->tcp->tcp_flags & TCPFLAGS_SYN) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit\n"); + PrintTcpPacket(p); + } + + StorePacket(p); + + ts1 = pi.ts.tv_sec + (double)pi.ts.tv_usec/1000000.0; + session.totSeenSent ++ ; + + } + + free(p); + continue; + + + } + + if (INSESSION(p, session.dst, session.dport, session.src, session.sport)) { + + /* Is it a SYN/ACK? */ + if (p->tcp->tcp_flags & (TCPFLAGS_SYN | TCPFLAGS_ACK)) { + + timeoutTime = GetTime(); /* force exit */ + synAckReceived++; + ts2 = pi.ts.tv_sec + (double)pi.ts.tv_usec/1000000.0; + session.rtt = ts2 - ts1 ; + + if (numRetransmits > 0) { + session.rtt_unreliable = 1; + printf("##### Unreliable\n"); /* ACK for which SYN? */ + } + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd:\n"); + PrintTcpPacket(p); + printf("Connection setup took %d ms\n",(int)((ts2 - ts1) * 1000.0)); + } + + StorePacket(p); + + /* Save ttl for,admittedly poor,indications of reverse route change */ + session.ttl = p->ip->ip_ttl; + session.snd_wnd = ntohl(p->tcp->tcp_win); + session.totRcvd++; + + free(p); + break ; + + } + + } + + free(p->ip); + free(p->tcp); + free(p); + + } + + } + + if (!synAckReceived) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("SYN timeout. Retransmitting\n"); + } + + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen, + TCPFLAGS_SYN | tcp_flags, + ip_optlen, /* IP opt len */ + optlen, /* TCP opt len */ + iptos); + + timeoutTime = GetTime() + SYNTIMEOUT; + numRetransmits++; + } + } + + if (numRetransmits >= MAXSYNRETRANSMITS) { + printf("ERROR: Could not establish contact after %d retries\nRETURN CODE: %d\n", + numRetransmits, NO_CONNECTION); + Quit(NO_CONNECTION); + } + + /* Update session variables */ + session.irs = ntohl(p->tcp->tcp_seq); + session.dataRcvd[0] = 1 ; + session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */ + session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */ + session.snd_una = session.iss + 1; + session.maxseqseen = ntohl(p->tcp->tcp_seq); + + session.initSession = 1; + if (session.debug >= SESSION_DEBUG_LOW) { + printf("src = %s:%d (%u)\n", InetAddress(session.src), session.sport, session.iss); + printf("dst = %s:%d (%u)\n", InetAddress(session.dst), session.dport, session.irs); + } + + free(synPacket->ip); + free(synPacket->tcp); + free(synPacket); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of EstablishSession...\n"); + } + + session.start_time = GetTime(); + + return 1; + +} + +int PrepareRequest(char *data, char *filename) +{ + + char h1[] = "GET "; + char h2[] = " HTTP/1.1"; + char h3[] = "Host: "; + char h4[] = "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11; DigExt; TBIT)"; + char h5[] = "Accept: */*"; + + /* New */ + char h7[] = "Pragma: no-cache"; + char h8[] = "Cache-control: no-chache"; + char deffile[] = DEFAULT_FILENAME; + + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In PrepareRequest...\n"); + } + + if (filename == NULL) { + filename = deffile; + } + + + if (strlen(session.targetName) > 0) { + + sprintf(data, + + "%s/%s %s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s%s\r\n\r\n", + h1, + filename, + h2, + h4, + h7, + h8, + h5, + h3, + session.targetName); + }else { + + sprintf(data, + "%s%s%s\r\n%s\r\n\r\n", + h1, + filename, + h2, + h4); + } + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out PrepareRequest...\n"); + } + + return ((int)strlen(data)); + +} + + +void SendRequest(char *filename, void (*ackData)(struct IPPacket *p)) +{ + + struct IPPacket *p, *datapkt; + struct pcap_pkthdr pi; + char *read_packet; + int i; + int sendflag = 1; + double startTime = 0; + char *dataptr; + char data[MAXREQUESTLEN]; + int datalen; + int ipsz; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendRequest...\n"); + } + + datalen = PrepareRequest(data, filename); + + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1; + + /* Allocate space for IP data packet */ + datapkt = AllocateIPPacket(0, 0, datalen + 1, "SendRequest (Data)"); + + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr, (void *)data, datalen); + + /* Send the data packet. Try to "achieve" reliability by sending the + * packet upto 5 times, wating for 2 seconds between packets (BAD + * busy-wait loop) + */ + + i = 0 ; + while(1) { + + if (sendflag == 1) { + + SendSessionPacket(datapkt, + ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK, + 0, /* ip opt len */ + 0, /* tcp opt len */ + 0); /* tos */ + + startTime = GetTime(); + sendflag = 0 ; + i++; + + } + + /* Check if we have received any packets */ + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * packet that we sent? + */ + + if (INSESSION(p,session.src,session.sport,session.dst,session.dport) && + (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) && + (ntohl(p->tcp->tcp_seq) == session.snd_nxt) && + (ntohl(p->tcp->tcp_ack) <= session.rcv_nxt)) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit %d\n", i); + PrintTcpPacket(p); + } + + StorePacket(p); + + free(p); + + //session.snd_nxt += datalen + 1; + session.totSeenSent++; + continue; + + } + /* + * packet from them? + */ + + if (INSESSION(p,session.dst,session.dport,session.src,session.sport) && + (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) && + (ntohl(p->tcp->tcp_ack) >= session.snd_una)) { + + + session.snd_una = ntohl(p->tcp->tcp_ack); + + if (p->ip->ip_ttl != session.ttl) { + printf("#### WARNING: route may have changed (ttl was %d, is %d).\n", + session.ttl, p->ip->ip_ttl); + session.ttl = p->ip->ip_ttl; + } + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd %d\n", i); + PrintTcpPacket(p); + } + + StorePacket(p); + session.totRcvd ++; + session.snd_nxt += datalen + 1; + + /* if the packet also contains data, receive it and send an ack if needed */ + (*ackData)(p); + + free(p); + break; + + } + + free(p); + + } + + if ((GetTime() - startTime >= REXMITDELAY) && + (sendflag == 0) && (i < MAXDATARETRANSMITS)) { + sendflag = 1 ; + } + + if (i >= MAXDATARETRANSMITS) { + printf ("ERROR: sent request 5 times without response\nRETURN CODE: %d\n", + SEND_REQUEST_FAILED); + Quit(SEND_REQUEST_FAILED); + } + + } + + free(datapkt->ip); + free(datapkt->tcp); + free(datapkt); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of SendRequest...\n"); + } +} + +void SendSessionPacket(struct IPPacket *p, + uint16 ip_len, uint8 tcp_flags, uint16 ip_optlen, uint16 optlen, + uint8 iptos) +{ + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendSessionPacket...\n"); + } + WriteIPPacket(p, + session.src, session.dst, session.sport, session.dport, + session.snd_nxt, session.rcv_nxt, tcp_flags, + session.rcv_wnd, 0, + (ip_len - sizeof(struct IpHeader) - ip_optlen - sizeof(struct TcpHeader) - optlen), + ip_optlen, optlen, iptos, 0); + + + /* Store packet here rather than in rcvData() because otherwise some + * ACKs may not be accounted for upon receiving reordered packets */ + + StorePacket(p); + + SendPkt(p, + ip_len, /* Total IP datagram size */ + ip_optlen, /* ip options len */ + optlen); /* tcp options len */ + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of SendSessionPacket...\n"); + } + +} + + +void SendICMPReply(struct IPPacket *p) +{ + + struct ICMPUnreachableErrorPacket *icmp_pkt; + int icmpsz; + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendICMPReply...\n"); + } + + icmpsz = sizeof(struct ICMPUnreachableErrorPacket); + if ((icmp_pkt = (struct ICMPUnreachableErrorPacket *)calloc(icmpsz + 1, 1)) == NULL) { + perror("ERROR: no space for ICMP packet:"); + Quit(ERR_MEM_ALLOC) ; + } + + /* Fill IP Header of ICMP packet */ + bzero((char *)icmp_pkt, sizeof(struct ICMPUnreachableErrorPacket)); + icmp_pkt->ip.ip_src = ip->ip_dst; + icmp_pkt->ip.ip_dst = ip->ip_src; + icmp_pkt->ip.ip_p = IPPROTOCOL_ICMP; + icmp_pkt->ip.ip_xsum = + htons((uint16)(sizeof(struct IcmpHeader) + sizeof(struct IpHeader) + sizeof(struct IpHeader) + 8)); /* pseudo hdr */ + icmp_pkt->ip.ip_ttl = 60; + icmp_pkt->ip.ip_tos = 0x00; + icmp_pkt->ip.ip_vhl = 0x45; +#ifdef __FreeBSD__ + icmp_pkt->ip.ip_off = IP_DF; + icmp_pkt->ip.ip_len = (uint16)(sizeof(struct ICMPUnreachableErrorPacket)); +#else /* __FreeBSD__ */ + icmp_pkt->ip.ip_off = htons(IP_DF); + icmp_pkt->ip.ip_len = htons((uint16)((sizeof (struct ICMPUnreachableErrorPacket) + 8 + 1))); +#endif /* __FreeBSD__ */ + + /* Fill ICMP header */ + icmp_pkt->icmp.icmp_type = 0x3; + icmp_pkt->icmp.icmp_code = 0x4; + icmp_pkt->icmp.icmp_xsum = 0; + icmp_pkt->icmp.icmp_unused = 0; + icmp_pkt->icmp.icmp_mtu = htons(session.mtu); + + /* Fill in ip header of offending packet */ + icmp_pkt->off_ip.ip_src = ip->ip_src; + icmp_pkt->off_ip.ip_dst = ip->ip_dst; + icmp_pkt->off_ip.ip_p = ip->ip_p; + icmp_pkt->off_ip.ip_xsum = ip->ip_xsum; + icmp_pkt->off_ip.ip_ttl = ip->ip_ttl; + icmp_pkt->off_ip.ip_tos = ip->ip_tos; + icmp_pkt->off_ip.ip_vhl = ip->ip_vhl; + icmp_pkt->off_ip.ip_p = ip->ip_p; +#ifdef __FreeBSD__ + icmp_pkt->off_ip.ip_off = ntohs(ip->ip_off); + icmp_pkt->off_ip.ip_len = ntohs(ip->ip_len); +#else /* __FreeBSD__ */ + icmp_pkt->off_ip.ip_off = ip->ip_off; + icmp_pkt->off_ip.ip_len = ip->ip_len; +#endif /* __FreeBSD__ */ + + icmp_pkt->tcp_sport = tcp->tcp_sport; + icmp_pkt->tcp_dport = tcp->tcp_dport; + icmp_pkt->tcp_seqno = (uint32)tcp->tcp_seq; + + icmp_pkt->icmp.icmp_xsum = InetChecksum((uint16 *)(&(icmp_pkt->icmp)), NULL, + (uint16)(sizeof(struct IcmpHeader) + sizeof(struct IpHeader) + 8), 0); + + if (session.verbose) { + printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + printf("TCP Packet: %lu\n", sizeof(struct IPPacket)); + PrintTcpPacket(p); + printf("ICMP Packet: %lu\n", sizeof(struct ICMPUnreachableErrorPacket)); + PrintICMPUnreachableErrorPacket(icmp_pkt); + printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + } + + SendICMPPkt(icmp_pkt, sizeof(struct ICMPUnreachableErrorPacket)); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of SendICMPReply...\n"); + } + +} + +void SendPkt(struct IPPacket *p, uint16 ip_len, int ip_optlen, + int tcp_optlen) { + int nbytes, datalen; + struct sockaddr_in sockAddr; + char *assembled_pkt; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendPkt...\n"); + } + /* Assemble contiguos packet to be sent */ + if ((assembled_pkt = (char *)calloc(1, ip_len)) == NULL) { + printf("SendPkt: Cannot allocate memory for assembled packet\n"); + Quit(ERR_MEM_ALLOC); + } + /* Copy IP Header and options, if any */ + memcpy((char *)assembled_pkt, (char *)(p->ip), + sizeof(struct IpHeader) + ip_optlen); + + /* Copy TCP Header and options, if any */ + memcpy((char *)(assembled_pkt + sizeof(struct IpHeader) + ip_optlen), + (char *)(p->tcp), + sizeof(struct TcpHeader) + tcp_optlen); + + /* Copy data bytes, if any */ + datalen = ip_len - ((sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + tcp_optlen)); + + if (datalen > 0) { + memcpy((char *)assembled_pkt + sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + tcp_optlen, + (char *)p->tcp + sizeof(struct TcpHeader) + tcp_optlen, datalen); + } + + + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = session.dst; + + if ((nbytes = (int)sendto(session.socket, + (char *)assembled_pkt, + ip_len, + 0, + (struct sockaddr *)&sockAddr, + sizeof(sockAddr))) < ip_len) { + printf("#### WARNING: only sent %d of %d bytes\n", nbytes, ip_len); + perror("here"); + + } + + session.totSent++; + + free(assembled_pkt); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out SendPkt...\n"); + } + +} + + + +void SendICMPPkt(struct ICMPUnreachableErrorPacket *p, uint16 len) { + + ssize_t nbytes; + struct sockaddr_in sockAddr; + + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = session.dst; + + nbytes = sendto(session.socket, (char *)p, len, 0, + (struct sockaddr *)&sockAddr, + sizeof(sockAddr)); + + if (nbytes < len) { + printf("#### WARNING: only sent %zd of %d (errno: %d) bytes\n", + nbytes, len, errno); + perror("here"); + } + + session.totSent++ ; + +} + +void rcvData (void (*ackData)(struct IPPacket *p)) +{ + + struct pcap_pkthdr pi; + struct IPPacket *p; + char *read_packet; + double startTime = GetTime () ; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In rcvData...\n"); + } + + while (1) { + + if ((GetTime() - startTime) > (MAXDATARETRANSMITS * REXMITDELAY)) { + printf ("ERROR: no Data received for %f seconds\nRETURN CODE: %d\n", + (MAXDATARETRANSMITS*REXMITDELAY), NO_DATA_RCVD); + Quit(NO_DATA_RCVD) ; + } + + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * Packet that we sent? + */ + + if (INSESSION(p,session.src,session.sport,session.dst,session.dport) && + ((p->tcp->tcp_flags & TCPFLAGS_ACK) || (p->tcp->tcp_flags & TCPFLAGS_FIN)) && + (ntohl(p->tcp->tcp_seq) == session.snd_nxt) && + (ntohl(p->tcp->tcp_ack) <= session.rcv_nxt)) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit:\n"); + PrintTcpPacket(p); + } + + session.totSeenSent++ ; + + free(p); + continue; + + } + + /* + * Data that we were expecting? + */ + + if (INSESSION(p,session.dst,session.dport,session.src,session.sport) && + (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_ack) >= session.snd_una)) { + + if (p->ip->ip_ttl != session.ttl) { + printf("#### WARNING: route may have changed (ttl was %d, is %d).\n", + session.ttl, p->ip->ip_ttl); + session.ttl = p->ip->ip_ttl; + } + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd: \n"); + PrintTcpPacket(p); + } + + session.totRcvd++ ; + startTime = GetTime () ; + StorePacket(p); + + /* if the packet also contains data, receive it, and send an ack if needed */ + ECNAckData(p); + + free(p); + continue ; + + } else { + + free(p); + + } + } + } +} + + diff --git a/network_cmds/ecnprobe/session.h b/network_cmds/ecnprobe/session.h new file mode 100644 index 0000000..bff3296 --- /dev/null +++ b/network_cmds/ecnprobe/session.h @@ -0,0 +1,183 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#define MAXREQUESTLEN 1000 + +#define SESSION_DEBUG_LOW 1 +#define SESSION_DEBUG_MEDIUM 2 +#define SESSION_DEBUG_HIGH 3 + +struct TcpSession { + + /* target name, as specified by the user */ + char targetName[MAXHOSTNAMELEN]; + + /* DNS name of hosts */ + char targetHostName[MAXHOSTNAMELEN]; + char sourceHostName[MAXHOSTNAMELEN]; + + /* raw socket we use to send on */ + int socket; + + /* connection endpoint identifiers */ + u_int32_t src; + u_int16_t sport; + u_int32_t dst; + u_int16_t dport; + + /* sender info, from RFC 793 */ + u_int32_t iss; // initial send sequence + u_int32_t snd_una; // sequence numbers of unacknowledged data + u_int32_t snd_nxt; // sequence number to be sent next + u_int16_t snd_wnd; + u_int16_t sndmss; + + /* Receiver info */ + u_int32_t irs; + u_int32_t rcv_wnd; + u_int32_t rcv_nxt; + u_int32_t maxseqseen; + u_int16_t mss; + + /* timing */ + double rtt; + u_int8_t ttl; + double start_time; + + /* data buffer */ + u_int8_t *dataRcvd ; + + /* basic results */ + int totSent; + int totRcvd; + int totSeenSent; + int totDataPktsRcvd; + int totOutofSeq; + int hsz; + + /* basic control*/ + int epochTime; + int debug; + int verbose; + int initSession; + int initCapture; + int initFirewall; + int firewall_rule_number; + char *filename; + int maxpkts; + + /* New loss-rate parameters */ + float loss_rate; + float prop_delay; + + /* results are suspect for various reasons */ + int rtt_unreliable; + int ignore_result; + + /* Drops and reordering startistics */ + int num_reordered; + int num_unwanted_drops; + int num_rtos; + int num_reord_ret; + int num_dup_transmissions; + int num_dup_acks; + int num_pkts_0_dup_acks; + int num_pkts_1_dup_acks; + int num_pkts_2_dup_acks; + int num_pkts_3_dup_acks; + int num_pkts_4_or_more_dup_acks; + int num_dupack_ret; + + /* For PMTUD test */ + int mtu; + + /* For ByteCounting test */ + int bytecounting_type; + int ack_bytes; /* How many bytes covered per ACK */ + int ack_rate; /* ACK [every | every other | every third |...] packet */ + + /* For WindowScale Option test */ + u_int8_t receiving_shift_count; + u_int8_t sending_shift_count; + + /* For MidBoxTTL test */ + int curr_ttl; + + int dont_send_reset; +}; + +//void SendSessionPacket(struct IPPacket *packet, +void SendSessionPacket(struct IPPacket *packet, + u_int16_t ip_len, /* Total size of IP datagram */ + u_int8_t tcp_flags, + u_int16_t ip_optlen, /* IP options len - New */ + u_int16_t optlen, /* TCP options len */ + u_int8_t iptos); + +void SendICMPReply(struct IPPacket *p); + +void SendPkt(struct IPPacket *p, u_int16_t ip_len, int ip_optlen, int tcp_optlen); + +void SendICMPPkt(struct ICMPUnreachableErrorPacket *p, u_int16_t ip_len); + +void StorePacket (struct IPPacket *p); + +int EstablishSession(u_int32_t sourceAddress, \ + u_int16_t sourcePort, \ + u_int32_t targetAddress, + u_int16_t targetPort, \ + int ip_optlen,\ + char *ip_opt,\ + int mss, + int optlen, + char *opt, \ + int maxwin, + int maxpkts, + u_int8_t iptos, + u_int8_t tcp_flags); + +void rcvData (void (*ackData)(struct IPPacket *p)); + +void SendRequest(char *filename, void (*ackData)(struct IPPacket *p)); + +int PrepareRequest(char *data, char *filename) ; diff --git a/network_cmds/ecnprobe/support.c b/network_cmds/ecnprobe/support.c new file mode 100644 index 0000000..2cdb405 --- /dev/null +++ b/network_cmds/ecnprobe/support.c @@ -0,0 +1,246 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" + +extern struct TcpSession session; + +void SendReset() +{ + struct IPPacket *p; + int i; + + if (session.dont_send_reset) + return; + + if ((p = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) == NULL) { + perror("ERROR: Could not allocate RST packet:") ; + Quit(ERR_MEM_ALLOC) ; + } + + if ((p->ip = (struct IpHeader *)calloc(1, sizeof(struct IpHeader))) == NULL) { + perror("ERROR: Could not allocate IP Header for RST packet:") ; + Quit(ERR_MEM_ALLOC) ; + } + + if ((p->tcp = (struct TcpHeader *)calloc(1, sizeof(struct TcpHeader))) == NULL) { + perror("ERROR: Could not allocate TCP Header for RST packet:") ; + Quit(ERR_MEM_ALLOC) ; + } + + for (i = 0; i < MAXRESETRETRANSMITS; i++) { + SendSessionPacket(p, + //sizeof(struct IPPacket), + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + TCPFLAGS_RST, + 0, + 0, + 0); + } + +/* free(p->ip); + free(p->tcp); + free(p); +*/ + +} + +#if 0 +/* make a clean exit on interrupts */ +void SigHandle (int signo) +{ + Cleanup () ; + fflush(stdout); + fflush(stderr); + exit(-1); +} + + +void Cleanup() +{ + + char ipfw_rule[100]; + int r; + + /* If a firewall rule has been installed then remove it */ + if (session.initFirewall > 0) { + +#ifdef linux +#define IP_FW_DEL (IP_FW_DELETE) +#endif /* linux */ + + sprintf(ipfw_rule, "ipfw del 00%d", session.firewall_rule_number); + r = system(ipfw_rule); + + } + + if (session.initSession > 0) { + + SendReset(); + shutdown(session.socket,2); + + } + + if (session.initCapture > 0) { + CaptureEnd(); + } + +} + +void Quit(int how) +{ + + Cleanup(); + fflush(stdout); + fflush(stderr); + exit(how); + +} +#endif /* 0 */ + +double GetTime() +{ + struct timeval tv; + struct timezone tz; + double postEpochSecs; + + if (gettimeofday(&tv, &tz) < 0) { + perror("GetTime"); + exit(-1); + } + + postEpochSecs = (double)tv.tv_sec + ((double)tv.tv_usec/(double)1000000.0); + return postEpochSecs; +} + +double GetTimeMicroSeconds() +{ + struct timeval tv; + struct timezone tz; + double postEpochMicroSecs; + + if (gettimeofday(&tv, &tz) < 0) { + perror("GetTimeMicroSeconds"); + exit(-1); + } + + postEpochMicroSecs = (double)tv.tv_sec * 1000000 + (double)tv.tv_usec; + return postEpochMicroSecs; + +} + +void PrintTimeStamp(struct timeval *ts) +{ + (void)printf("%02d:%02d:%02d.%06u ", + (unsigned int)ts->tv_sec / 3600, + ((unsigned int)ts->tv_sec % 3600) / 60, + (unsigned int)ts->tv_sec % 60, (unsigned int)ts->tv_usec); +} + +void processBadPacket (struct IPPacket *p) +{ + + if (session.debug == SESSION_DEBUG_HIGH) { + printf("In ProcessBadPacket...\n"); + } + /* + * reset? the other guy does not like us? + */ + if (INSESSION(p,session.dst,session.dport,session.src,session.sport) && (p->tcp->tcp_flags & TCPFLAGS_RST)) { + printf("ERROR: EARLY_RST.\nRETURN CODE: %d\n", EARLY_RST); + Quit(EARLY_RST); + } + /* + * some other packet between us that is none of the above + */ + if (INSESSION(p, session.src, session.sport, session.dst, session.dport) || + INSESSION(p, session.dst, session.dport, session.src, session.sport)) { + + printf("ERROR: Unexpected packet\nRETURN CODE: %d\n", UNEXPECTED_PKT); + printf("Expecting:\n"); + printf("\tsrc = %s:%d (seq=%u, ack=%u)\n", + InetAddress(session.src), session.sport, + session.snd_nxt-session.iss, + session.rcv_nxt-session.irs); + printf("\tdst = %s:%d (seq=%u, ack=%u)\n", + InetAddress(session.dst),session.dport, + session.rcv_nxt-session.irs, session.snd_una-session.iss); + printf("Received:\n\t"); + PrintTcpPacket(p); + printf ("session.snd_nxt=%d, session.rcv_nxt=%d, session.snd_una=%d\n", + session.snd_nxt-session.iss, session.rcv_nxt-session.irs, session.snd_una-session.iss); + Quit(UNEXPECTED_PKT); + } + /* + * none of the above, + * so we must be seeing packets + * from some other flow! + */ + else { + printf("ERRROR: Received packet from different flow\nRETURN CODE: %d\n", DIFF_FLOW); + PrintTcpPacket(p); + Quit(DIFF_FLOW) ; + } + + if (session.debug == SESSION_DEBUG_HIGH) { + printf("Out ProcessBadPacket...\n"); + } +} + +void busy_wait (double wait) +{ + double now = GetTime(); + double x = now ; + while ((x - now) < wait) { + x = GetTime(); + } +} diff --git a/network_cmds/ecnprobe/support.h b/network_cmds/ecnprobe/support.h new file mode 100644 index 0000000..94be2e5 --- /dev/null +++ b/network_cmds/ecnprobe/support.h @@ -0,0 +1,132 @@ + +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#include <signal.h> + +#define MAXRESETRETRANSMITS (3) +/*#define INSESSION(p, src, sport, dst, dport) \ + (((p)->ip.ip_src == (src)) && ((p)->ip.ip_dst == (dst)) && \ + ((p)->ip.ip_p == IPPROTOCOL_TCP) && \ + ((p)->tcp.tcp_sport == htons(sport)) && \ + ((p)->tcp.tcp_dport == htons(dport)))*/ + +#define INSESSION(p, src, sport, dst, dport) \ + (((p)->ip->ip_src == (src)) && ((p)->ip->ip_dst == (dst)) && \ + ((p)->ip->ip_p == IPPROTOCOL_TCP) && \ + ((p)->tcp->tcp_sport == htons(sport)) && \ + ((p)->tcp->tcp_dport == htons(dport))) + +#define SEQ_LT(a,b) ((int)((a)-(b)) < 0) +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define SEQ_GT(a,b) ((int)((a)-(b)) > 0) +#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0) + +#define DEFAULT_TARGETPORT (80) +#define DEFAULT_MSS 1360 +#define DEFAULT_MTU 1500 +#define RTT_TO_MULT 5 +#define PLOTDIFF 0.00009 + +/* Response codes */ +#define FAIL -1 +#define SUCCESS 0 +#define NO_TARGET_CANON_INFO 1 +#define NO_LOCAL_HOSTNAME 2 +#define NO_SRC_CANON_INFO 3 +#define NO_SESSION_ESTABLISH 4 +#define MSS_TOO_SMALL 5 +#define BAD_ARGS 6 +#define FIREWALL_ERR 7 +#define ERR_SOCKET_OPEN 8 +#define ERR_SOCKOPT 9 +#define ERR_MEM_ALLOC 10 +#define NO_CONNECTION 11 +#define MSS_ERR 12 +#define BUFFER_OVERFLOW 13 +#define UNWANTED_PKT_DROP 14 +#define EARLY_RST 15 +#define UNEXPECTED_PKT 16 +#define DIFF_FLOW 17 +#define ERR_CHECKSUM 18 +#define NOT_ENOUGH_PKTS 19 +#define BAD_OPT_LEN 20 +#define TOO_MANY_PKTS 21 +#define NO_DATA_RCVD 22 +#define NO_TRGET_SPECIFIED 23 +#define BAD_OPTIONS 24 +#define TOO_MANY_TIMEOUTS 25 +#define TOO_MANY_RXMTS 26 +#define NO_SACK 27 +#define ERR_IN_SB_CALC 28 +#define TOO_MANY_HOLES 29 +#define TOO_MANY_DROPS 30 +#define UNWANTED_PKT_REORDER 31 +#define NO_PMTUD_ENABLED 32 +#define UNKNOWN_BEHAVIOR 33 +#define NO_SYNACK_RCVD 34 +#define SEND_REQUEST_FAILED 35 +#define PKT_SIZE_CHANGED 36 +#define ECN_SYN_DROP 37 + +#define DEFAULT_FILENAME "/" + +#define RTT_TO_MULT 5 +#define SYNTIMEOUT (2.0) +#define REXMITDELAY (2.0) +#define MAXSYNRETRANSMITS (6) +#define MAXDATARETRANSMITS (6) + +/* HTTP Response Codes */ +#define HTTP_OK "200" + + +void SendReset(); +void SigHandle (int signo); +void Cleanup(); +void Quit(int how); +double GetTime(); +double GetTimeMicroSeconds(); +void PrintTimeStamp(struct timeval *ts); +void processBadPacket (struct IPPacket *p); +void busy_wait (double wait); diff --git a/network_cmds/frame_delay/frame_delay.8 b/network_cmds/frame_delay/frame_delay.8 new file mode 100644 index 0000000..0f454d5 --- /dev/null +++ b/network_cmds/frame_delay/frame_delay.8 @@ -0,0 +1,45 @@ +.Dd October 12, 2015 +.Dt FRAME_DELAY 8 +.Os Darwin +.Sh NAME +.Nm frame_delay +.Nd Utility to measure TCP/UDP frame delay + +.Sh DESCRIPTION +.Pp +The +.Nm +utility is designed to measure the effect of latency on +delivery of evenly spaced TCP/UDP frames. This can be latency induced +due to buffering or protocol stack or network drivers. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl m +Server/Client mode. Should be "server" or "client". +.It Fl t +TCP/UDP frame. Should be either "tcp" or "udp". +.It Fl i +(Client Only) Server ip address. +.It Fl p +Port number. +.It Fl n +Number of frames. +.It Fl f +Frame size. +.It Fl d +(Client only) Delay traffic class. Pick one from {BK_SYS, BK, BE, RD, QAM, AV, RV, VI, VO, CTL}. +.El + +.Sh EXAMPLES +.Pp +Setup TCP server: +.Dl "frame_delay -m server -t tcp -p 10010 -n 10 -f 1000" +.Pp +Setup corresponding TCP client: +.Dl "frame_delay -m client -t tcp -i 127.0.0.1 -p 10010 -n 10 -f 1000 -d 2000 -k RD" + +.Sh AUTHORS +.An Padma Bhooma , +.An Kang Sun , +.An Vincent Lubet .
\ No newline at end of file diff --git a/network_cmds/frame_delay/frame_delay.c b/network_cmds/frame_delay/frame_delay.c new file mode 100644 index 0000000..37a58b7 --- /dev/null +++ b/network_cmds/frame_delay/frame_delay.c @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2009-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Usage for frame_delay + * + * Server + * ./frame_delay -m server -t <tcp/udp> -p <port> -n <num_frames> -f <frame_size> + * + * Client + * ./frame_delay -m client -t <tcp/udp> -i <srv_ipv4_add> -p <srv_port> -n <num_frames> -f <frame_size> -d <delay_ms> -k <traffic_class> + */ + +/* + * TODO list : + * 1. UDP fragmentation and reassembly + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> + +/* Server Static variable */ +static int so, srv_so; +static int srv_port = 0; +static struct sockaddr_in laddr, dst_addr; +/* Client Static variable */ +static struct sockaddr_in srv_addr; +static uint32_t tc = 0; +/* Usage */ +void ErrorUsage(void); +/* str2svc */ +uint32_t str2svc(const char *str); +/* Show Stastics */ +void ShowStastics(int64_t *DiffsBuf, int num_frames); +/* Returns difference between two timevals in microseconds */ +int64_t time_diff(struct timeval *b, struct timeval *a); +/* tcp server */ +void tcpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf); +/* udp server */ +void udpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf); +/* tcp server */ +void tcpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time); +/* udp server */ +void udpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time); + +/* Main function */ +int +main(int argc, char *argv[]) +{ + int num_frames = 0, frame_size = 0, delay_ms = 0, rc = 0; + char *buf = NULL, ch, *type = NULL, *mode = NULL, *ip_addr = NULL; + int64_t *DiffsBuf; + struct timespec sleep_time; + + while ((ch = getopt(argc, argv, "m:p:f:n:t:d:i:k:")) != -1) { + switch (ch) { + case 'm': { + mode = optarg; + break; + } + case 'p': { + srv_port = atoi(optarg); + break; + } + case 'f' : { + frame_size = atoi(optarg); + break; + } + case 'n' : { + num_frames = atoi(optarg); + break; + } + case 'i': { + ip_addr = optarg; + bzero(&srv_addr, sizeof(srv_addr)); + rc = inet_aton(optarg, &srv_addr.sin_addr); + if (rc == 0) { + perror("inet_ntoa failed"); + exit(1); + } + } + case 'd': { + delay_ms = atoi(optarg); + break; + } + case 't' : { + type = optarg; + break; + } + case 'k': { + tc = str2svc(optarg); + break; + } + default: { + printf("Invalid option: %c\n", ch); + ErrorUsage(); + } + } + } + /* General check for both server and client */ + if (srv_port <= 0 || frame_size <= 0 || num_frames <= 0 || !mode || !type) { + ErrorUsage(); + } + if ( strcmp(type, "tcp") != 0 && strcmp(type, "udp") != 0 ) { + ErrorUsage(); + } + /* Allocate memory for buf */ + buf = calloc(1, frame_size); + if (buf == NULL) { + printf("malloc failed\n"); + exit(1); + } + if ( strcmp(mode, "server") == 0 ) { + /* Server */ + printf("<LOG> : Start %s server on port %d with expected frame size of %d\n", + type, srv_port, frame_size); + DiffsBuf = (int64_t *)calloc(num_frames, sizeof(int64_t)); + if (DiffsBuf == NULL) { + printf("malloc failed\n"); + exit(1); + } + if( strcmp(type, "tcp") == 0) { + /* tcpServer */ + tcpServer(frame_size, num_frames, buf, DiffsBuf); + } else { + /* updServer */ + udpServer(frame_size, num_frames, buf, DiffsBuf); + } + } + else if ( strcmp(mode, "client") == 0 ){ + if ( !ip_addr || (tc > 0 && (tc < SO_TC_BK_SYS || tc > SO_TC_CTL)) ){ + ErrorUsage(); + } + /* Client */ + printf("<LOG> : Start sending %d %s frames to %s:%d with a frame size of %d\n", + num_frames, type, ip_addr, srv_port, frame_size); + /* Resolving sleep time bug : delay_ms should just be calculated once */ + bzero(&sleep_time, sizeof(sleep_time)); + while (delay_ms >= 1000) { + sleep_time.tv_sec++; + delay_ms -= 1000; + } + sleep_time.tv_nsec = delay_ms * 1000 * 1000; + if( strcmp(type, "tcp") == 0) { + /* Call TCP client */ + tcpClient(num_frames, frame_size, buf, sleep_time); + } else { + /* Call UDP client */ + udpClient(num_frames, frame_size, buf, sleep_time); + } + } else { + ErrorUsage(); + } +} + +/* Error usage */ +void +ErrorUsage(void) { + printf("Correct Usage"); + printf("Server : frame_delay -m server -t <tcp/udp> -p <port> -n <num_frames> -f <frame_size>\n"); + printf("Client : frame_delay -m client -t <tcp/udp> -i <srv_ipv4_add> -p <srv_port> -n <num_frames> -f <frame_size> -d <delay_ms> -k <traffic_class>\n"); + exit(1); +} + +/* str2svc */ +uint32_t +str2svc(const char *str) +{ + uint32_t svc; + char *endptr; + + if (str == NULL || *str == '\0') + svc = UINT32_MAX; + else if (strcasecmp(str, "BK_SYS") == 0) + return SO_TC_BK_SYS; + else if (strcasecmp(str, "BK") == 0) + return SO_TC_BK; + else if (strcasecmp(str, "BE") == 0) + return SO_TC_BE; + else if (strcasecmp(str, "RD") == 0) + return SO_TC_RD; + else if (strcasecmp(str, "OAM") == 0) + return SO_TC_OAM; + else if (strcasecmp(str, "AV") == 0) + return SO_TC_AV; + else if (strcasecmp(str, "RV") == 0) + return SO_TC_RV; + else if (strcasecmp(str, "VI") == 0) + return SO_TC_VI; + else if (strcasecmp(str, "VO") == 0) + return SO_TC_VO; + else if (strcasecmp(str, "CTL") == 0) + return SO_TC_CTL; + else { + svc = (uint32_t)strtoul(str, &endptr, 0); + if (*endptr != '\0') + svc = UINT32_MAX; + } + return (svc); +} + +/* Show Stastics */ +void +ShowStastics(int64_t *DiffsBuf, int num_frames) { + int i = 0; + int64_t sum = 0, mean = 0; + + /* Mean */ + while(i < num_frames) + sum += DiffsBuf[i++]; + mean = sum / num_frames; + printf("<LOG> : Mean: %.2f usecs\n", sum / (double)num_frames); + /* Popular Standard Deviation */ + i = 0; + sum = 0; + while(i < num_frames) { + sum += (DiffsBuf[i]-mean)*(DiffsBuf[i]-mean); + i++; + } + printf("<LOG> : Popular Standard Deviation: %.2f usecs\n", + sqrt(sum/(double)num_frames)); +} + +/* Returns difference between two timevals in microseconds */ +int64_t +time_diff(struct timeval *b, struct timeval *a) +{ + int64_t usecs; + usecs = (a->tv_sec - b->tv_sec) * 1000 * 1000; + usecs += (int64_t)(a->tv_usec - b->tv_usec); + return(usecs); +} + +/* Server */ + +/* tcp server */ +void +tcpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf) { + int rc = 0, i = 0, ignore_count = 0; + uint32_t dst_len = 0; + struct timeval before, after; + ssize_t bytes; + int64_t usecs; + /* New change from Padama */ + uint64_t prev_frame_ts = 0, prev_recv = 0, frame_ts = 0, cur_recv = 0; + uint64_t min_variation = 0, max_variation = 0, avg_variation = 0; + + printf("<LOG> : TCP Server\n"); + so = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (so == -1) { + perror("failed to create socket"); + exit(1); + } + bzero(&laddr, sizeof(laddr)); + laddr.sin_family = AF_INET; + laddr.sin_port = htons(srv_port); + rc = bind(so, (const struct sockaddr *)&laddr, sizeof(laddr)); + if (rc != 0) { + perror("failed to bind"); + exit(1); + } + rc = listen(so, 10); + if (rc != 0) { + perror("failed to listen"); + exit(1); + } + srv_so = accept(so, (struct sockaddr *)&dst_addr, &dst_len); + if (srv_so == -1) { + perror("failed to accept"); + exit(1); + } + while (1) { + if ( i == num_frames ) { + printf("<LOG> : Completed\n"); + break; + } + printf("<LOG> : Waiting for receiving\n"); + bzero(&before, sizeof(before)); + bzero(&after, sizeof(after)); + rc = gettimeofday(&before, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + bytes = recv(srv_so, buf, frame_size, MSG_WAITALL); + if (bytes == -1) { + perror("recv failed"); + exit(1); + } + else if (bytes > 0 && bytes != frame_size) { + printf("Client exited\n"); + printf("Didn't recv the complete frame, bytes %ld\n", + bytes); + exit(1); + } + else if (bytes == 0) { + break; + } + rc = gettimeofday(&after, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + cur_recv = after.tv_sec * 1000 * 1000 + after.tv_usec; + memcpy((void *)&frame_ts, buf, sizeof(frame_ts)); + if (prev_frame_ts > 0) { + int64_t d_variation = 0; + d_variation = (int64_t)((cur_recv - prev_recv) - + (frame_ts - prev_frame_ts)); + /* printf("Frame %u ts %llu d_variation %lld usecs\n", + i, frame_ts, d_variation);*/ + if (d_variation > 0) { + if (min_variation == 0) + min_variation = d_variation; + else + min_variation = ((min_variation <= d_variation) ? + min_variation : d_variation); + max_variation = ((max_variation >= d_variation) ? + max_variation : d_variation); + avg_variation += d_variation; + } else { + ignore_count++; + } + } + prev_recv = cur_recv; + prev_frame_ts = frame_ts; + ++i; + /* Compute the time differenc */ + usecs = time_diff(&before, &after); + DiffsBuf[i] = usecs; + printf("<LOG> : Frame %d received after %lld usecs\n", i, usecs); + } + if (i != ignore_count) + avg_variation = avg_variation / (i - ignore_count); + else + avg_variation = 0; + + printf("<LOG> : Received frames: %u\n", i); + printf("<LOG> : Ignored frames: %u\n", ignore_count); + printf("<LOG> : Minimum delay variation: %llu usecs\n", min_variation); + printf("<LOG> : Maximum delay variation: %llu usecs\n", max_variation); + printf("<LOG> : Average delay variation: %llu usecs\n", avg_variation); + ShowStastics(DiffsBuf, num_frames); +} + +/* udp server */ +void +udpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf) { + int rc = 0, i = 0, ignore_count = 0; + uint32_t dst_len = 0; + ssize_t bytes; + struct timeval before, after; + int64_t usecs; + /* New change from Padama */ + uint64_t prev_frame_ts = 0, prev_recv = 0, frame_ts = 0, cur_recv = 0; + uint64_t min_variation = 0, max_variation = 0, avg_variation = 0; + + printf("<LOG> : UDP Server\n"); + so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (so == -1) { + perror("failed to create socket"); + exit(1); + } + bzero(&laddr,sizeof(laddr)); + laddr.sin_family = AF_INET; + laddr.sin_addr.s_addr=htonl(INADDR_ANY); + laddr.sin_port=htons(srv_port); + rc = bind(so, (struct sockaddr *)&laddr,sizeof(laddr)); + if (rc != 0) { + perror("failed to bind"); + exit(1); + } + while (1) { + if ( i == num_frames ) { + printf("<LOG> : Completed\n"); + break; + } + printf("<LOG> : Waiting for receiving\n"); + bzero(&before, sizeof(before)); + bzero(&after, sizeof(after)); + rc = gettimeofday(&before, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + bytes = recvfrom(so, buf, frame_size, 0, (struct sockaddr *)&dst_addr, &dst_len); + if (bytes == -1) { + perror("recv failed"); + exit(1); + } + else if (bytes > 0 && bytes != frame_size) { + printf("Client exited\n"); + printf("Didn't recv the complete frame, bytes %ld\n", + bytes); + exit(1); + } + else if (bytes == 0) { + break; + } + rc = gettimeofday(&after, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + cur_recv = after.tv_sec * 1000 * 1000 + after.tv_usec; + memcpy((void *)&frame_ts, buf, sizeof(frame_ts)); + if (prev_frame_ts > 0) { + int64_t d_variation = 0; + + d_variation = (int64_t)((cur_recv - prev_recv) - + (frame_ts - prev_frame_ts)); + /* printf("Frame %u ts %llu d_variation %lld usecs\n", + i, frame_ts, d_variation);*/ + if (d_variation > 0) { + if (min_variation == 0) + min_variation = d_variation; + else + min_variation = ((min_variation <= d_variation) ? + min_variation : d_variation); + max_variation = ((max_variation >= d_variation) ? + max_variation : d_variation); + avg_variation += d_variation; + } else { + ignore_count++; + } + } + prev_recv = cur_recv; + prev_frame_ts = frame_ts; + ++i; + /* Compute the time differenc */ + usecs = time_diff(&before, &after); + DiffsBuf[i] = usecs; + printf("<LOG> : Frame %d received after %lld usecs\n", i, usecs); + } + if (i != ignore_count) + avg_variation = avg_variation / (i - ignore_count); + else + avg_variation = 0; + printf("<LOG> : Received frames: %u\n", i); + printf("<LOG> : Ignored frames: %u\n", ignore_count); + printf("<LOG> : Minimum delay variation: %llu usecs\n", min_variation); + printf("<LOG> : Maximum delay variation: %llu usecs\n", max_variation); + printf("<LOG> : Average delay variation: %llu usecs\n", avg_variation); + ShowStastics(DiffsBuf, num_frames); +} + +/* Client */ +void +tcpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time){ + int rc = 0, i = 0; + ssize_t bytes; + + printf("<LOG> : TCP Client\n"); + so = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (so <= 0) { + perror("creating socket failed"); + exit(1); + } + srv_addr.sin_port = htons(srv_port); + srv_addr.sin_len = sizeof(srv_addr); + srv_addr.sin_family = AF_INET; + rc = connect(so, (const struct sockaddr *)&srv_addr, + sizeof(srv_addr)); + if (rc != 0) { + perror("connect failed"); + exit(1); + } + if (tc > 0) { + rc = setsockopt(so, SOL_SOCKET, SO_TRAFFIC_CLASS, &tc, + sizeof(tc)); + if (rc == -1) { + perror("failed to set traffic class"); + exit(1); + } + } + for (i = 0; i < num_frames; ++i) { + struct timeval fts; + uint64_t frame_ts; + /* Add a timestamp to the frame */ + rc = gettimeofday(&fts, NULL); + if (rc == -1) { + perror("faile to get time of day"); + exit(1); + } + frame_ts = fts.tv_sec * 1000 * 1000 + fts.tv_usec; + memcpy((void *)buf, (const void *)&frame_ts, sizeof(frame_ts)); + bytes = send(so, buf, frame_size, 0); + if (bytes == -1) { + perror("send failed \n"); + exit(1); + } + if (bytes != frame_size) { + printf("failed to send all bytes, sent %ld\n", bytes); + exit (1); + } + rc = nanosleep(&sleep_time, NULL); + if (rc == -1) { + perror("sleep failed"); + exit(1); + } + printf("<LOG> : Sent %u frames as a whole\n", (i + 1)); + } +} + +void +udpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time){ + int rc = 0, i = 0; + ssize_t bytes; + + printf("<LOG> : UDP Client\n"); + so = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (so <= 0) { + perror("creating socket failed"); + exit(1); + } + srv_addr.sin_port = htons(srv_port); + srv_addr.sin_len = sizeof(srv_addr); + srv_addr.sin_family = AF_INET; + if (tc > 0) { + rc = setsockopt(so, SOL_SOCKET, SO_TRAFFIC_CLASS, &tc, + sizeof(tc)); + if (rc == -1) { + perror("failed to set traffic class"); + exit(1); + } + } + for (i = 0; i < num_frames; ++i) { + struct timeval fts; + uint64_t frame_ts; + /* Add a timestamp to the frame */ + rc = gettimeofday(&fts, NULL); + if (rc == -1) { + perror("faile to get time of day"); + exit(1); + } + frame_ts = fts.tv_sec * 1000 * 1000 + fts.tv_usec; + memcpy((void *)buf, (const void *)&frame_ts, sizeof(frame_ts)); + bytes = sendto(so, buf, frame_size, 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); + if (bytes == -1) { + perror("send failed \n"); + exit(1); + } + if (bytes != frame_size) { + printf("failed to send all bytes, sent %ld\n", bytes); + exit (1); + } + rc = nanosleep(&sleep_time, NULL); + if (rc == -1) { + perror("sleep failed"); + exit(1); + } + printf("<LOG> : Sent %u frames as a whole\n", (i + 1)); + } +} + + diff --git a/network_cmds/ifconfig.tproj/af_inet.c b/network_cmds/ifconfig.tproj/af_inet.c new file mode 100644 index 0000000..621bd16 --- /dev/null +++ b/network_cmds/ifconfig.tproj/af_inet.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2009-2011, 2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <netinet/in.h> +#include <net/if_var.h> /* for struct ifaddr */ +#include <netinet/in_var.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "ifconfig.h" + +static struct ifaliasreq in_addreq; +static struct ifreq in_ridreq; + +static void +in_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_in *sin, null_sin; + + memset(&null_sin, 0, sizeof(null_sin)); + + sin = (struct sockaddr_in *)ifa->ifa_addr; + if (sin == NULL) + return; + + printf("\tinet %s ", inet_ntoa(sin->sin_addr)); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sin = (struct sockaddr_in *)ifa->ifa_dstaddr; + if (sin == NULL) + sin = &null_sin; + printf("--> %s ", inet_ntoa(sin->sin_addr)); + } + + sin = (struct sockaddr_in *)ifa->ifa_netmask; + if (sin == NULL) + sin = &null_sin; + printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr)); + + if (ifa->ifa_flags & IFF_BROADCAST) { + sin = (struct sockaddr_in *)ifa->ifa_broadaddr; + if (sin != NULL && sin->sin_addr.s_addr != 0) + printf("broadcast %s", inet_ntoa(sin->sin_addr)); + } + putchar('\n'); +} + +#define SIN(x) ((struct sockaddr_in *) &(x)) +static struct sockaddr_in *sintab[] = { + SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), + SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) +}; + +static void +in_getaddr(const char *s, int which) +{ +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif /* MIN */ + struct sockaddr_in *sin = sintab[which]; + struct hostent *hp; + struct netent *np; + + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + + if (which == ADDR) { + char *p = NULL; + + if((p = strrchr(s, '/')) != NULL) { + /* address is `name/masklen' */ + int masklen; + int ret; + struct sockaddr_in *min = sintab[MASK]; + *p = '\0'; + ret = sscanf(p+1, "%u", &masklen); + if(ret != 1 || (masklen < 0 || masklen > 32)) { + *p = '/'; + errx(1, "%s: bad value", s); + } + min->sin_len = sizeof(*min); + min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & + 0xffffffff); + } + } + + if (inet_aton(s, &sin->sin_addr)) + return; + if ((hp = gethostbyname(s)) != 0) + bcopy(hp->h_addr, (char *)&sin->sin_addr, + MIN(hp->h_length, sizeof(sin->sin_addr))); + else if ((np = getnetbyname(s)) != 0) + sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); + else + errx(1, "%s: bad value", s); +#undef MIN +} + +static void +in_status_tunnel(int s) +{ + char src[NI_MAXHOST]; + char dst[NI_MAXHOST]; + struct ifreq ifr; + const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) + return; + if (sa->sa_family != AF_INET) + return; + if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) + src[0] = '\0'; + + if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) + return; + if (sa->sa_family != AF_INET) + return; + if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) + dst[0] = '\0'; + + printf("\ttunnel inet %s --> %s\n", src, dst); +} + +static void +in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) +{ + struct ifaliasreq addreq; + + memset(&addreq, 0, sizeof(addreq)); + strlcpy(addreq.ifra_name, name, sizeof(addreq.ifra_name)); + memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); + memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); + + if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0) + warn("SIOCSIFPHYADDR"); +} + +static void +in_set_router(int s, int enable) +{ + struct ifreq ifr; + + bzero(&ifr, sizeof (ifr)); + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_intval = enable; + + if (ioctl(s, SIOCSETROUTERMODE, &ifr) < 0) + warn("SIOCSETROUTERMODE"); +} + +static int +routermode_from_string(char * str, int *mode_p) +{ + int success = 1; + + if (strcasecmp(str, "enabled") == 0) { + *mode_p = 1; + } else if (strcasecmp(str, "disabled") == 0) { + *mode_p = 0; + } else { + success = 0; + } + return (success); +} + +static const char * +routermode_string(int mode) +{ + const char * str; + + switch (mode) { + case 0: + str = "disabled"; + break; + case 1: + str = "enabled"; + break; + default: + str = "<unknown>"; + break; + } + return str; +} + +static int +in_routermode(int s, int argc, char *const*argv) +{ + struct ifreq ifr; + int ret; + + bzero(&ifr, sizeof (ifr)); + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (argc == 0) { + ret = 0; +#ifndef SIOCGETROUTERMODE +#define SIOCGETROUTERMODE _IOWR('i', 209, struct ifreq) /* get IPv4 router mode state */ +#endif /* SIOCGETROUTERMODE */ + if (ioctl(s, SIOCGETROUTERMODE, &ifr) < 0) { + if (argv != NULL) { + warn("SIOCGETROUTERMODE"); + } + } else { + /* argv is NULL if we're called from status() */ + printf("%s%s\n", + (argv == NULL) ? "\troutermode4: " : "", + routermode_string(ifr.ifr_intval)); + } + ret = 0; + } else { + int mode; + + if (routermode_from_string(argv[0], &mode) == 0) { + errx(EXIT_FAILURE, + "mode '%s' invalid, must be one of " + "disabled or enabled", + argv[0]); + } + ifr.ifr_intval = mode; + if (ioctl(s, SIOCSETROUTERMODE, &ifr) < 0) { + warn("SIOCSETROUTERMODE"); + } + ret = 1; + } + return ret; +} + +static struct afswtch af_inet = { + .af_name = "inet", + .af_af = AF_INET, + .af_status = in_status, + .af_getaddr = in_getaddr, + .af_status_tunnel = in_status_tunnel, + .af_settunnel = in_set_tunnel, + .af_setrouter = in_set_router, + .af_routermode = in_routermode, + .af_difaddr = SIOCDIFADDR, + .af_aifaddr = SIOCAIFADDR, + .af_ridreq = &in_ridreq, + .af_addreq = &in_addreq, +}; + +static __constructor void +inet_ctor(void) +{ + af_register(&af_inet); +} diff --git a/network_cmds/ifconfig.tproj/af_inet6.c b/network_cmds/ifconfig.tproj/af_inet6.c new file mode 100644 index 0000000..c32c5c0 --- /dev/null +++ b/network_cmds/ifconfig.tproj/af_inet6.c @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2009-2017, 2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <arpa/inet.h> + +#include <netinet/in.h> +#include <net/if_var.h> /* for struct ifaddr */ +#include <netinet/in_var.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */ + +#include "ifconfig.h" + +#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ + "\004IFDISABLED\005DONT_SET_IFROUTE\006PROXY_PREFIXES" \ + "\007IGNORE_NA\010INSECURE\011REPLICATED\012DAD" + +static struct in6_ifreq in6_ridreq; +static struct in6_aliasreq in6_addreq = + { { 0 }, + { 0 }, + { 0 }, + { 0 }, + 0, + { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; +static int ip6lifetime; + +static void in6_fillscopeid(struct sockaddr_in6 *sin6); +static int prefix(void *, int); +static char *sec2str(time_t); +static int explicit_prefix = 0; + +static char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/ + +static void +setifprefixlen(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getprefix != NULL) + afp->af_getprefix(addr, MASK); + explicit_prefix = 1; +} + +static void +setnd6flags(const char *dummyaddr __unused, int d, int s, + const struct afswtch *afp) +{ + struct in6_ndireq nd; + int error; + + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname)); + error = ioctl(s, SIOCGIFINFO_IN6, &nd); + if (error) { + warn("ioctl(SIOCGIFINFO_IN6)"); + return; + } + if (d < 0) + nd.ndi.flags &= ~(-d); + else + nd.ndi.flags |= d; + error = ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd); + if (error) + warn("ioctl(SIOCSIFINFO_FLAGS)"); +} + +static void +setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused, + const struct afswtch *afp) +{ + if (afp->af_af != AF_INET6) + err(1, "address flags can be set only for inet6 addresses"); + + if (flag < 0) + in6_addreq.ifra_flags &= ~(-flag); + else + in6_addreq.ifra_flags |= flag; +} + +static void +setip6lifetime(const char *cmd, const char *val, int s, + const struct afswtch *afp) +{ + time_t newval, t; + char *ep; + + t = time(NULL); + newval = (time_t)strtoul(val, &ep, 0); + if (val == ep) + errx(1, "invalid %s", cmd); + if (afp->af_af != AF_INET6) + errx(1, "%s not allowed for the AF", cmd); + if (strcmp(cmd, "vltime") == 0) { + in6_addreq.ifra_lifetime.ia6t_expire = t + newval; + in6_addreq.ifra_lifetime.ia6t_vltime = newval; + } else if (strcmp(cmd, "pltime") == 0) { + in6_addreq.ifra_lifetime.ia6t_preferred = t + newval; + in6_addreq.ifra_lifetime.ia6t_pltime = newval; + } +} + +static void +setip6pltime(const char *seconds, int dummy __unused, int s, + const struct afswtch *afp) +{ + setip6lifetime("pltime", seconds, s, afp); +} + +static void +setip6vltime(const char *seconds, int dummy __unused, int s, + const struct afswtch *afp) +{ + setip6lifetime("vltime", seconds, s, afp); +} + +static void +setip6eui64(const char *cmd, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifaddrs *ifap, *ifa; + const struct sockaddr_in6 *sin6 = NULL; + const struct in6_addr *lladdr = NULL; + struct in6_addr *in6; + + if (afp->af_af != AF_INET6) + errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); + in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; + if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) + errx(EXIT_FAILURE, "interface index is already filled"); + if (getifaddrs(&ifap) != 0) + err(EXIT_FAILURE, "getifaddrs"); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family == AF_INET6 && + strcmp(ifa->ifa_name, name) == 0) { + sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + lladdr = &sin6->sin6_addr; + break; + } + } + } + if (!lladdr) + errx(EXIT_FAILURE, "could not determine link local address"); + + memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); + + freeifaddrs(ifap); +} + +static void +in6_fillscopeid(struct sockaddr_in6 *sin6) +{ +#if defined(__KAME__) && defined(KAME_SCOPEID) + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; + } +#endif +} + +static void +in6_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_in6 *sin, null_sin; + struct in6_ifreq ifr6; + int s6; + u_int32_t flags6; + struct in6_addrlifetime lifetime; + time_t t = time(NULL); + int error; + u_int32_t scopeid; + + memset(&null_sin, 0, sizeof(null_sin)); + + sin = (struct sockaddr_in6 *)ifa->ifa_addr; + if (sin == NULL) + return; + + strlcpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + warn("socket(AF_INET6,SOCK_DGRAM)"); + return; + } + ifr6.ifr_addr = *sin; + if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + warn("ioctl(SIOCGIFAFLAG_IN6)"); + close(s6); + return; + } + flags6 = ifr6.ifr_ifru.ifru_flags6; + memset(&lifetime, 0, sizeof(lifetime)); + ifr6.ifr_addr = *sin; + if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { + warn("ioctl(SIOCGIFALIFETIME_IN6)"); + close(s6); + return; + } + lifetime = ifr6.ifr_ifru.ifru_lifetime; + close(s6); + + /* XXX: embedded link local addr check */ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && + *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { + u_short index; + + index = *(u_short *)&sin->sin6_addr.s6_addr[2]; + *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = ntohs(index); + } + scopeid = sin->sin6_scope_id; + + error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, + sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); + if (error != 0) + inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, + sizeof(addr_buf)); + printf("\tinet6 %s ", addr_buf); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr; + /* + * some of the interfaces do not have valid destination + * address. + */ + if (sin != NULL && sin->sin6_family == AF_INET6) { + int error; + + /* XXX: embedded link local addr check */ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && + *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { + u_short index; + + index = *(u_short *)&sin->sin6_addr.s6_addr[2]; + *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = ntohs(index); + } + + error = getnameinfo((struct sockaddr *)sin, + sin->sin6_len, addr_buf, + sizeof(addr_buf), NULL, 0, + NI_NUMERICHOST); + if (error != 0) + inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, + sizeof(addr_buf)); + printf("--> %s ", addr_buf); + } + } + + sin = (struct sockaddr_in6 *)ifa->ifa_netmask; + if (sin == NULL) + sin = &null_sin; + printf("prefixlen %d ", prefix(&sin->sin6_addr, + sizeof(struct in6_addr))); + + if ((flags6 & IN6_IFF_ANYCAST) != 0) + printf("anycast "); + if ((flags6 & IN6_IFF_TENTATIVE) != 0) + printf("tentative "); + if ((flags6 & IN6_IFF_OPTIMISTIC) != 0) + printf("optimistic "); + if ((flags6 & IN6_IFF_DUPLICATED) != 0) + printf("duplicated "); + if ((flags6 & IN6_IFF_DETACHED) != 0) + printf("detached "); + if ((flags6 & IN6_IFF_DEPRECATED) != 0) + printf("deprecated "); + if ((flags6 & IN6_IFF_AUTOCONF) != 0) + printf("autoconf "); + if ((flags6 & IN6_IFF_TEMPORARY) != 0) + printf("temporary "); + if ((flags6 & IN6_IFF_DYNAMIC) != 0) + printf("dynamic "); + if ((flags6 & IN6_IFF_SECURED) != 0) + printf("secured "); + if ((flags6 & IN6_IFF_CLAT46) != 0) + printf("clat46 "); + + if (scopeid) + printf("scopeid 0x%x ", scopeid); + + if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { + printf("pltime "); + if (lifetime.ia6t_preferred) { + printf("%s ", lifetime.ia6t_preferred < t + ? "0" : sec2str(lifetime.ia6t_preferred - t)); + } else + printf("infty "); + + printf("vltime "); + if (lifetime.ia6t_expire) { + printf("%s ", lifetime.ia6t_expire < t + ? "0" : sec2str(lifetime.ia6t_expire - t)); + } else + printf("infty "); + } + + putchar('\n'); +} + +#define SIN6(x) ((struct sockaddr_in6 *) &(x)) +static struct sockaddr_in6 *sin6tab[] = { + SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), + SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr) +}; + +static void +in6_getprefix(const char *plen, int which) +{ + struct sockaddr_in6 *sin = sin6tab[which]; + u_char *cp; + int len = atoi(plen); + + if ((len < 0) || (len > 128)) + errx(1, "%s: bad value", plen); + sin->sin6_len = sizeof(*sin); + if (which != MASK) + sin->sin6_family = AF_INET6; + if ((len == 0) || (len == 128)) { + memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); + return; + } + memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); + for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) + *cp++ = 0xff; + *cp = 0xff << (8 - len); +} + +static void +in6_getaddr(const char *s, int which) +{ + struct sockaddr_in6 *sin = sin6tab[which]; + struct addrinfo hints, *res; + int error = -1; + + newaddr &= 1; + + sin->sin6_len = sizeof(*sin); + if (which != MASK) + sin->sin6_family = AF_INET6; + + if (which == ADDR) { + char *p = NULL; + if((p = strrchr(s, '/')) != NULL) { + *p = '\0'; + in6_getprefix(p + 1, MASK); + explicit_prefix = 1; + } + } + + if (sin->sin6_family == AF_INET6) { + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + error = getaddrinfo(s, NULL, &hints, &res); + } + if (error != 0) { + if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) + errx(1, "%s: bad value", s); + } else + bcopy(res->ai_addr, sin, res->ai_addrlen); +} + +static int +prefix(void *val, int size) +{ + u_char *name = (u_char *)val; + int byte, bit, plen = 0; + + for (byte = 0; byte < size; byte++, plen += 8) + if (name[byte] != 0xff) + break; + if (byte == size) + return (plen); + for (bit = 7; bit != 0; bit--, plen++) + if (!(name[byte] & (1 << bit))) + break; + for (; bit != 0; bit--) + if (name[byte] & (1 << bit)) + return(0); + byte++; + for (; byte < size; byte++) + if (name[byte]) + return(0); + return (plen); +} + +static char * +sec2str(time_t total) +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + if (0) { + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + } else + snprintf(result, sizeof(result), "%lu", (unsigned long)total); + + return(result); +} + +static void +in6_postproc(int s, const struct afswtch *afp) +{ + if (explicit_prefix == 0) { + /* Aggregatable address architecture defines all prefixes + are 64. So, it is convenient to set prefixlen to 64 if + it is not specified. */ + setifprefixlen("64", 0, s, afp); + /* in6_getprefix("64", MASK) if MASK is available here... */ + } +} + +static void +in6_status_tunnel(int s) +{ + char src[NI_MAXHOST]; + char dst[NI_MAXHOST]; + struct in6_ifreq in6_ifr; + const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; + + memset(&in6_ifr, 0, sizeof(in6_ifr)); + strlcpy(in6_ifr.ifr_name, name, sizeof(in6_ifr.ifr_name)); + + if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) + return; + if (sa->sa_family != AF_INET6) + return; + in6_fillscopeid(&in6_ifr.ifr_addr); + if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, + NI_NUMERICHOST) != 0) + src[0] = '\0'; + + if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) + return; + if (sa->sa_family != AF_INET6) + return; + in6_fillscopeid(&in6_ifr.ifr_addr); + if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, + NI_NUMERICHOST) != 0) + dst[0] = '\0'; + + printf("\ttunnel inet6 %s --> %s\n", src, dst); +} + +static void +nd6_status(int s) +{ + struct in6_ndireq nd; + int s6; + int error; + + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname)); + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + if (errno != EPROTONOSUPPORT) + warn("socket(AF_INET6, SOCK_DGRAM)"); + return; + } + error = ioctl(s6, SIOCGIFINFO_IN6, &nd); + if (error) { + if (errno != EPFNOSUPPORT && errno != EINVAL) + warn("ioctl(SIOCGIFINFO_IN6)"); + close(s6); + return; + } + close(s6); + if (nd.ndi.flags == 0) + return; + printb("\tnd6 options", (unsigned int)nd.ndi.flags, ND6BITS); + putchar('\n'); +} + +static void +in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) +{ + struct in6_aliasreq in6_addreq; + + memset(&in6_addreq, 0, sizeof(in6_addreq)); + strlcpy(in6_addreq.ifra_name, name, sizeof(in6_addreq.ifra_name)); + memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); + memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, + dstres->ai_addr->sa_len); + + if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) + warn("SIOCSIFPHYADDR_IN6"); +} + +#ifndef IPV6_ROUTER_MODE_EXCLUSIVE +#define IPV6_ROUTER_MODE_DISABLED 0 +#define IPV6_ROUTER_MODE_EXCLUSIVE 1 +#define IPV6_ROUTER_MODE_HYBRID 2 +#endif /* IPV6_ROUTER_MODE_EXCLUSIVE */ + +static void +in6_set_router(int s, int enable) +{ + struct ifreq ifr; + + bzero(&ifr, sizeof (ifr)); + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_intval = (enable == 0) + ? IPV6_ROUTER_MODE_DISABLED + : IPV6_ROUTER_MODE_EXCLUSIVE; + + if (ioctl(s, SIOCSETROUTERMODE_IN6, &ifr) < 0) + warn("SIOCSETROUTERMODE_IN6"); +} + +static int +routermode_from_string(char * str, int *mode_p) +{ + int success = 1; + + if (strcasecmp(str, "exclusive") == 0 || + strcasecmp(str, "enabled") == 0) { + *mode_p = IPV6_ROUTER_MODE_EXCLUSIVE; + } else if (strcasecmp(str, "hybrid") == 0) { + *mode_p = IPV6_ROUTER_MODE_HYBRID; + } else if (strcasecmp(str, "disabled") == 0) { + *mode_p = IPV6_ROUTER_MODE_DISABLED; + } else { + success = 0; + } + return (success); +} + +static const char * +routermode_string(int mode) +{ + const char * str; + + switch (mode) { + case IPV6_ROUTER_MODE_EXCLUSIVE: + str = "enabled"; + break; + case IPV6_ROUTER_MODE_HYBRID: + str = "hybrid"; + break; + case IPV6_ROUTER_MODE_DISABLED: + str = "disabled"; + break; + default: + str = "<unknown>"; + break; + } + return str; +} + +static int +in6_routermode(int s, int argc, char *const*argv) +{ + struct in6_ifreq ifr; + int ret; + + bzero(&ifr, sizeof (ifr)); + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (argc == 0) { + ret = 0; +#ifndef SIOCGETROUTERMODE_IN6 +#define SIOCGETROUTERMODE_IN6 _IOWR('i', 137, struct in6_ifreq) +#endif /* SIOCGETROUTERMODE_IN6 */ + if (ioctl(s, SIOCGETROUTERMODE_IN6, &ifr) < 0) { + if (argv != NULL) { + warn("SIOCGETROUTERMODE_IN6"); + } + } else { + /* argv is NULL if we're called from status() */ + printf("%s%s\n", + (argv == NULL) ? "\troutermode6: " : "", + routermode_string(ifr.ifr_intval)); + } + ret = 0; + } else { + int mode; + + if (routermode_from_string(argv[0], &mode) == 0) { + errx(EXIT_FAILURE, + "mode '%s' invalid, must be one of " + "disabled, exclusive, or hybrid", + argv[0]); + } + ifr.ifr_intval = mode; + if (ioctl(s, SIOCSETROUTERMODE_IN6, &ifr) < 0) { + warn("SIOCSETROUTERMODE_IN6"); + } + ret = 1; + } + return ret; +} + +static struct cmd inet6_cmds[] = { + DEF_CMD_ARG("prefixlen", setifprefixlen), + DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), + DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), + DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), + /* RFC 4429, section 3.1, says: + * "Optimistic DAD SHOULD NOT be used for manually entered + * addresses." + * it's not a MUST... + */ + DEF_CMD("optimistic", IN6_IFF_OPTIMISTIC, setip6flags), + DEF_CMD("-optimistic", -IN6_IFF_OPTIMISTIC, setip6flags), + DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), + DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), + DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), + DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), + DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), + DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), + DEF_CMD("replicated", ND6_IFF_REPLICATED, setnd6flags), + DEF_CMD("-replicated", -ND6_IFF_REPLICATED, setnd6flags), + DEF_CMD("proxy_prefixes", ND6_IFF_PROXY_PREFIXES, setnd6flags), + DEF_CMD("-proxy_prefixes", -ND6_IFF_PROXY_PREFIXES, setnd6flags), + DEF_CMD("insecure", ND6_IFF_INSECURE, setnd6flags), + DEF_CMD("-insecure", -ND6_IFF_INSECURE, setnd6flags), + DEF_CMD_ARG("pltime", setip6pltime), + DEF_CMD_ARG("vltime", setip6vltime), + DEF_CMD("eui64", 0, setip6eui64), + DEF_CMD("secured", IN6_IFF_SECURED, setip6flags), + DEF_CMD("-secured", -IN6_IFF_SECURED, setip6flags), + DEF_CMD("dad", ND6_IFF_DAD, setnd6flags), + DEF_CMD("-dad", -ND6_IFF_DAD, setnd6flags), +}; + +static struct afswtch af_inet6 = { + .af_name = "inet6", + .af_af = AF_INET6, + .af_status = in6_status, + .af_getaddr = in6_getaddr, + .af_getprefix = in6_getprefix, + .af_other_status = nd6_status, + .af_postproc = in6_postproc, + .af_status_tunnel = in6_status_tunnel, + .af_settunnel = in6_set_tunnel, + .af_setrouter = in6_set_router, + .af_routermode = in6_routermode, + .af_difaddr = SIOCDIFADDR_IN6, + .af_aifaddr = SIOCAIFADDR_IN6, + .af_ridreq = &in6_ridreq, + .af_addreq = &in6_addreq, +}; + +static void +in6_Lopt_cb(const char *optarg __unused) +{ + ip6lifetime++; /* print IPv6 address lifetime */ +} +static struct option in6_Lopt = { "L", "[-L]", in6_Lopt_cb }; + +static __constructor void +inet6_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(inet6_cmds); i++) + cmd_register(&inet6_cmds[i]); + af_register(&af_inet6); + opt_register(&in6_Lopt); +#undef N +} diff --git a/network_cmds/ifconfig.tproj/af_link.c b/network_cmds/ifconfig.tproj/af_link.c new file mode 100644 index 0000000..ca9444c --- /dev/null +++ b/network_cmds/ifconfig.tproj/af_link.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ifaddrs.h> + +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/ethernet.h> + +#include "ifconfig.h" + +static struct ifreq link_ridreq; + +static void +link_status(int s __unused, const struct ifaddrs *ifa) +{ + /* XXX no const 'cuz LLADDR is defined wrong */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr; + + if (sdl != NULL && sdl->sdl_alen > 0) { +#ifdef notyet + if (sdl->sdl_type == IFT_ETHER && + sdl->sdl_alen == ETHER_ADDR_LEN) + printf("\tether %s\n", + ether_ntoa((struct ether_addr *)LLADDR(sdl))); + else { + int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; + + printf("\tlladdr %s\n", link_ntoa(sdl) + n); + } +#else + char *cp = (char *)LLADDR(sdl); + int n = sdl->sdl_alen; + + if (sdl->sdl_type == IFT_ETHER) + printf ("\tether "); + else + printf ("\tlladdr "); + while (--n >= 0) + printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' '); + putchar('\n'); +#endif + } +} + +static void +link_getaddr(const char *addr, int which) +{ + char *temp; + struct sockaddr_dl sdl; + struct sockaddr *sa = &link_ridreq.ifr_addr; + size_t slen = strlen(addr); + + if (which != ADDR) + errx(1, "can't set link-level netmask or broadcast"); + if ((temp = malloc(slen + 2)) == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strlcpy(temp + 1, addr, slen + 1); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen > sizeof(sa->sa_data)) + errx(1, "malformed link-level address"); + sa->sa_family = AF_LINK; + sa->sa_len = sdl.sdl_alen; + bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); +} + +static struct afswtch af_link = { + .af_name = "link", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; +static struct afswtch af_ether = { + .af_name = "ether", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; +static struct afswtch af_lladdr = { + .af_name = "lladdr", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; + +static __constructor void +link_ctor(void) +{ + af_register(&af_link); + af_register(&af_ether); + af_register(&af_lladdr); +} diff --git a/network_cmds/ifconfig.tproj/if6lowpan.c b/network_cmds/ifconfig.tproj/if6lowpan.c new file mode 100644 index 0000000..f0f4b2e --- /dev/null +++ b/network_cmds/ifconfig.tproj/if6lowpan.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017-2018 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_6lowpan_var.h> +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +#include <sys/cdefs.h> + + +static boolean_t is_sixlowpan_inited = FALSE; +static struct sixlowpanreq params; + +static int +get6lowpan(int s, struct ifreq *ifr, struct sixlowpanreq *req) +{ + bzero((char *)req, sizeof(*req)); + ifr->ifr_data = (caddr_t)req; + return ioctl(s, SIOCGIF6LOWPAN, (caddr_t)ifr); +} + +static void +sixlowpan_status(int s) +{ + struct sixlowpanreq req; + + if (get6lowpan(s, &ifr, &req) != -1) + printf("\t6lowpan: parent interface: %s\n", + req.parent[0] == '\0' ? + "<none>" : req.parent); +} + + +static void +set6lowpandev(const char *val, int d, int s, const struct afswtch *afp) +{ + struct sixlowpanreq req; + + strlcpy(params.parent, val, sizeof(params.parent)); + is_sixlowpan_inited = TRUE; + fprintf(stderr, "val %s\n", val); + + strlcpy(req.parent, val, sizeof(req.parent)); + ifr.ifr_data = (caddr_t) &req; + if (ioctl(s, SIOCSIF6LOWPAN, (caddr_t)&ifr) == -1) + err(1, "SIOCSIF6LOWPAN"); +} + +static void +unset6lowpandev(const char *val, int d, int s, const struct afswtch *afp) +{ + struct sixlowpanreq req; + + bzero((char *)&req, sizeof(req)); + ifr.ifr_data = (caddr_t)&req; + + if (ioctl(s, SIOCGIF6LOWPAN, (caddr_t)&ifr) == -1) + err(1, "SIOCGIF6LOWPAN"); + + bzero((char *)&req, sizeof(req)); + if (ioctl(s, SIOCSIF6LOWPAN, (caddr_t)&ifr) == -1) + err(1, "SIOCSIF6LOWPAN"); +} + +static void +sixlowpan_create(const char *val, int d, int s, const struct afswtch *afp) +{ + strlcpy(params.parent, val, sizeof(params.parent)); + is_sixlowpan_inited = TRUE; +} + +static void +sixlowpan_clone_cb(int s, void *arg) +{ + if (is_sixlowpan_inited == TRUE && params.parent[0] == '\0') + errx(1, "6lowpandev must be specified"); +} + +static struct cmd sixlowpan_cmds[] = { + DEF_CLONE_CMD_ARG("6lowpandev", sixlowpan_create), + DEF_CMD_OPTARG("6lowpansetdev", set6lowpandev), + DEF_CMD_OPTARG("6lowpanunsetdev", unset6lowpandev), +}; +static struct afswtch af_6lowpan = { + .af_name = "af_6lowpan", + .af_af = AF_UNSPEC, + .af_other_status = sixlowpan_status, +}; + +static __constructor void +sixlowpan_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(sixlowpan_cmds); i++) + cmd_register(&sixlowpan_cmds[i]); + af_register(&af_6lowpan); + callback_register(sixlowpan_clone_cb, NULL); +#undef N +} diff --git a/network_cmds/ifconfig.tproj/ifbond.c b/network_cmds/ifconfig.tproj/ifbond.c new file mode 100644 index 0000000..b8bcdfe --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifbond.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * ifbond.c + * - add and remove interfaces from a bond interface + */ + +/* + * Modification History: + * + * July 14, 2004 Dieter Siegmund (dieter@apple.com) + * - created + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_bond_var.h> + +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" +extern int bond_details; + +#define EA_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x" +#define EA_CH(e, i) ((u_char)((u_char *)(e))[(i)]) +#define EA_LIST(ea) EA_CH(ea,0),EA_CH(ea,1),EA_CH(ea,2),EA_CH(ea,3),EA_CH(ea,4),EA_CH(ea,5) + +static __inline__ const char * +selected_state_string(u_char s) +{ + static const char * names[] = { "unselected", "selected", "standby" }; + + if (s <= IF_BOND_STATUS_SELECTED_STATE_STANDBY) { + return (names[s]); + } + return ("<unknown>"); +} + +static void +bond_print_details(struct if_bond_status * ibs_p, int count) + +{ + int i; + struct if_bond_status * scan_p = ibs_p; + + for (i = 0; i < count; i++, scan_p++) { + struct if_bond_partner_state * ps; + ps = &scan_p->ibs_partner_state; + printf("\tbond interface: %s priority: 0x%04x " + "state: 0x%02x partner system: 0x%04x," + EA_FORMAT " " + "key: 0x%04x port: 0x%04x priority: 0x%04x " + "state: 0x%02x\n", + scan_p->ibs_if_name, scan_p->ibs_port_priority, + scan_p->ibs_state, ps->ibps_system_priority, + EA_LIST(&ps->ibps_system), ps->ibps_key, + ps->ibps_port, ps->ibps_port_priority, + ps->ibps_state); + } + return; +} + +void +bond_status(int s) +{ + int i; + struct if_bond_req ibr; + struct if_bond_status * ibs_p; + struct if_bond_status_req * ibsr_p; + char mode_buf[16]; + const char * mode_str; + + bzero((char *)&ibr, sizeof(ibr)); + ibr.ibr_op = IF_BOND_OP_GET_STATUS; + ibsr_p = &ibr.ibr_ibru.ibru_status; + ibsr_p->ibsr_version = IF_BOND_STATUS_REQ_VERSION; + ifr.ifr_data = (caddr_t)&ibr; + + /* how many of them are there? */ + if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) < 0) { + return; + } + switch (ibsr_p->ibsr_mode) { + case IF_BOND_MODE_LACP: + mode_str = "lacp"; + break; + case IF_BOND_MODE_STATIC: + mode_str = "static"; + break; + default: + snprintf(mode_buf, sizeof(mode_buf), "%d", ibsr_p->ibsr_mode); + mode_str = mode_buf; + break; + } + if (ibsr_p->ibsr_total == 0) { + if (bond_details) { + printf("\tbond mode: %s\n" + "\tbond key: 0x%04x interfaces: <none>", + mode_str, ibsr_p->ibsr_key); + } + else { + printf("\tbond interfaces: <none>\n"); + } + return; + } + ibsr_p->ibsr_buffer + = (char *)malloc(sizeof(struct if_bond_status) + * ibsr_p->ibsr_total); + ibsr_p->ibsr_count = ibsr_p->ibsr_total; + + /* get the list */ + if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) < 0) { + goto done; + } + if (ibsr_p->ibsr_total > 0) { + if (bond_details) { + printf("\tbond mode: %s\n" + "\tbond key: 0x%04x interfaces:", + mode_str, ibsr_p->ibsr_key); + } + else { + printf("\tbond interfaces:"); + } + ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer; + for (i = 0; i < ibsr_p->ibsr_total; i++, ibs_p++) { + printf(" %s", ibs_p->ibs_if_name); + if (bond_details) { + u_char s = ibs_p->ibs_selected_state; + printf(" (%s)", selected_state_string(s)); + } + } + printf("\n"); + if (bond_details) { + bond_print_details((struct if_bond_status *) + ibsr_p->ibsr_buffer, + ibsr_p->ibsr_total); + } + } + else if (bond_details) { + printf("\tbond mode: %s\n" + "\tbond key: 0x%04x interfaces: <none>\n", + mode_str, ibsr_p->ibsr_key); + } + else { + printf("\tbond interfaces: <none>\n"); + } + + done: + free(ibsr_p->ibsr_buffer); + return; +} + +static +DECL_CMD_FUNC(setbonddev, val, d) +{ + struct if_bond_req ibr; + + bzero((char *)&ibr, sizeof(ibr)); + if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name, + sizeof(ibr.ibr_ibru.ibru_if_name), + "%s", val) >= IFNAMSIZ) { + errx(1, "interface name too long"); + } + ibr.ibr_op = IF_BOND_OP_ADD_INTERFACE; + ifr.ifr_data = (caddr_t)&ibr; + if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) + err(1, "SIOCSIFBOND add interface"); + + return; +} + +static +DECL_CMD_FUNC(unsetbonddev, val, d) +{ + struct if_bond_req ibr; + + bzero((char *)&ibr, sizeof(ibr)); + if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name, + sizeof(ibr.ibr_ibru.ibru_if_name), + "%s", val) >= IFNAMSIZ) { + errx(1, "interface name too long"); + } + ibr.ibr_op = IF_BOND_OP_REMOVE_INTERFACE; + ifr.ifr_data = (caddr_t)&ibr; + if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) + err(1, "SIOCSIFBOND remove interface"); + + return; +} + +static +DECL_CMD_FUNC(setbondmode, val, d) +{ + struct if_bond_req ibr; + int mode; + + if (strcmp(val, "lacp") == 0) { + mode = IF_BOND_MODE_LACP; + } + else if (strcmp(val, "static") == 0) { + mode = IF_BOND_MODE_STATIC; + } + else { + mode = strtoul(val, NULL, 0); + if (errno != 0) { + errx(1, "invalid mode value " + "(must be either \"lacp\" or \"static\")"); + } + } + + bzero((char *)&ibr, sizeof(ibr)); + if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name, + sizeof(ibr.ibr_ibru.ibru_if_name), + "%s", val) >= IFNAMSIZ) { + errx(1, "interface name too long"); + } + ibr.ibr_op = IF_BOND_OP_SET_MODE; + ibr.ibr_ibru.ibru_int_val = mode; + ifr.ifr_data = (caddr_t)&ibr; + if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) + err(1, "SIOCSIFBOND set mode"); + + return; +} + +static struct cmd bond_cmds[] = { + DEF_CLONE_CMD_ARG("bonddev", setbonddev), + DEF_CLONE_CMD_ARG("-bonddev", unsetbonddev), + DEF_CMD_ARG("bondmode", setbondmode), +}; +static struct afswtch af_bond = { + .af_name = "af_bond", + .af_af = AF_UNSPEC, + .af_other_status = bond_status, +}; + +static __constructor void +bond_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(bond_cmds); i++) + cmd_register(&bond_cmds[i]); + af_register(&af_bond); +#undef N +} + diff --git a/network_cmds/ifconfig.tproj/ifbridge.c b/network_cmds/ifconfig.tproj/ifbridge.c new file mode 100644 index 0000000..951c56b --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifbridge.c @@ -0,0 +1,952 @@ +/* + * Copyright (c) 2009-2019 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/*- + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_bridgevar.h> +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include <arpa/inet.h> + +#include "ifconfig.h" + +#define PV2ID(pv, epri, eaddr) do { \ + epri = pv >> 48; \ + eaddr[0] = pv >> 40; \ + eaddr[1] = pv >> 32; \ + eaddr[2] = pv >> 24; \ + eaddr[3] = pv >> 16; \ + eaddr[4] = pv >> 8; \ + eaddr[5] = pv >> 0; \ +} while (0) + +static const char *stpstates[] = { + "disabled", + "listening", + "learning", + "forwarding", + "blocking", + "discarding" +}; +static const char *stpproto[] = { + "stp", + "-", + "rstp" +}; +static const char *stproles[] = { + "disabled", + "root", + "designated", + "alternate", + "backup" +}; + +static int +get_val(const char *cp, u_long *valp) +{ + char *endptr; + u_long val; + + errno = 0; + val = strtoul(cp, &endptr, 0); + if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) + return (-1); + + *valp = val; + return (0); +} + +static int +do_cmd(int sock, u_long op, void *arg, size_t argsize, int set) +{ + struct ifdrv ifd; + + memset(&ifd, 0, sizeof(ifd)); + + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = op; + ifd.ifd_len = argsize; + ifd.ifd_data = arg; + + return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); +} + +static void +do_bridgeflag(int sock, const char *ifs, int flag, int set) +{ + struct ifbreq req; + + strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); + + if (do_cmd(sock, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) + err(1, "unable to get bridge flags"); + + if (set) + req.ifbr_ifsflags |= flag; + else + req.ifbr_ifsflags &= ~flag; + + if (do_cmd(sock, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) + err(1, "unable to set bridge flags"); +} + +static void +bridge_interfaces(int s, const char *prefix) +{ + struct ifbifconf bifc; + struct ifbreq *req; + char *inbuf = NULL, *ninbuf; + char *p, *pad; + int i, len = 8192; + + pad = strdup(prefix); + if (pad == NULL) + err(1, "strdup"); + /* replace the prefix with whitespace */ + for (p = pad; *p != '\0'; p++) { + if(isprint(*p)) + *p = ' '; + } + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate interface buffer"); + bifc.ifbic_len = len; + bifc.ifbic_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGGIFS, &bifc, sizeof(bifc), 0) < 0) + err(1, "unable to get interface list"); + if ((bifc.ifbic_len + sizeof(*req)) < len) + break; + len *= 2; + } + + for (i = 0; i < bifc.ifbic_len / sizeof(*req); i++) { + req = bifc.ifbic_req + i; + printf("%s%s ", prefix, req->ifbr_ifsname); + printb("flags", req->ifbr_ifsflags, IFBIFBITS); + printf("\n"); + + printf("%s", pad); + printf("ifmaxaddr %u", req->ifbr_addrmax); + printf(" port %u priority %u", req->ifbr_portno, + req->ifbr_priority); + printf(" path cost %u", req->ifbr_path_cost); + + if (req->ifbr_ifsflags & IFBIF_STP) { + if (req->ifbr_proto < + sizeof(stpproto) / sizeof(stpproto[0])) + printf(" proto %s", stpproto[req->ifbr_proto]); + else + printf(" <unknown proto %d>", + req->ifbr_proto); + + printf("\n%s", pad); + if (req->ifbr_role < + sizeof(stproles) / sizeof(stproles[0])) + printf("role %s", stproles[req->ifbr_role]); + else + printf("<unknown role %d>", + req->ifbr_role); + if (req->ifbr_state < + sizeof(stpstates) / sizeof(stpstates[0])) + printf(" state %s", stpstates[req->ifbr_state]); + else + printf(" <unknown state %d>", + req->ifbr_state); + } + printf("\n"); + + if (verbose) { + struct ifbrhostfilter ifbrfh; + struct in_addr in; + struct ether_addr ea; + + bzero(&ifbrfh, sizeof(struct ifbrhostfilter)); + strlcpy(ifbrfh.ifbrhf_ifsname, req->ifbr_ifsname, sizeof(ifbrfh.ifbrhf_ifsname)); + if (do_cmd(s, BRDGGHOSTFILTER, &ifbrfh, sizeof(ifbrfh), 0) < 0) + err(1, "unable to get host filter settings for %s", + ifbrfh.ifbrhf_ifsname); + + if (ifbrfh.ifbrhf_flags & IFBRHF_ENABLED) { + in.s_addr = ifbrfh.ifbrhf_ipsrc; + bcopy(ifbrfh.ifbrhf_hwsrca, ea.octet, ETHER_ADDR_LEN); + } else { + in.s_addr = INADDR_ANY; + bzero(ea.octet, ETHER_ADDR_LEN); + } + printf("%s", pad); + printf("hostfilter %d hw: %s ip: %s", + ifbrfh.ifbrhf_flags & IFBRHF_ENABLED ? 1 : 0, + ether_ntoa(&ea), inet_ntoa(in)); + + printf("\n"); + } + } + + free(inbuf); + free(pad); +} + +static void +bridge_addresses(int s, const char *prefix) +{ + struct ifbaconf ifbac; + struct ifbareq *ifba; + char *inbuf = NULL, *ninbuf; + int i, len = 8192; + struct ether_addr ea; + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate address buffer"); + ifbac.ifbac_len = len; + ifbac.ifbac_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) + err(1, "unable to get address cache"); + if ((ifbac.ifbac_len + sizeof(*ifba)) < len) + break; + len *= 2; + } + + for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { + ifba = ifbac.ifbac_req + i; + memcpy(ea.octet, ifba->ifba_dst, + sizeof(ea.octet)); + printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea), + ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire); + printb("flags", ifba->ifba_flags, IFBAFBITS); + printf("\n"); + } + + free(inbuf); +} + +#define MAX_IPv6_STR_LEN INET6_ADDRSTRLEN +static void +bridge_mac_nat(int s, const char *prefix) +{ + char *buf; + unsigned int count; + struct ether_addr ea; + unsigned int i; + struct ifbrmnelist mnl; + char *scan; + + bzero(&mnl, sizeof(mnl)); + if (do_cmd(s, BRDGGMACNATLIST, &mnl, sizeof(mnl), 0) < 0) { + /* err(1, "unable to get mac nat list"); */ + return; + } + if (mnl.ifbml_len == 0) { + return; + } + printf("\tMAC NAT list:\n"); + if (mnl.ifbml_elsize == 0) { + err(1, "kernel reported zero length element size"); + } + if (mnl.ifbml_elsize < sizeof(struct ifbrmne)) { + err(1, "struct element size too small, kernel mismatch"); + } + buf = malloc(mnl.ifbml_len); + if (buf == NULL) { + err(1, "unable to allocate mac nat list buffer"); + } + mnl.ifbml_buf = buf; + if (do_cmd(s, BRDGGMACNATLIST, &mnl, sizeof(mnl), 0) < 0) { + err(1, "unable to get mac nat list"); + } + count = mnl.ifbml_len / mnl.ifbml_elsize; + for (i = 0, scan = buf; i < count; i++, scan += mnl.ifbml_elsize) { + struct ifbrmne *ifbmne = (struct ifbrmne *)scan; + char ntopbuf[INET6_ADDRSTRLEN]; + + memcpy(ea.octet, ifbmne->ifbmne_mac, + sizeof(ea.octet)); + inet_ntop(ifbmne->ifbmne_af, &ifbmne->ifbmne_ip, + ntopbuf, sizeof(ntopbuf)); + printf("%s%s %s %s %lu\n", + prefix, ifbmne->ifbmne_ifname, ntopbuf, ether_ntoa(&ea), + (unsigned long)ifbmne->ifbmne_expire); + } + free(buf); +} + +static void +bridge_status(int s) +{ + struct ifbropreq ifbp; + struct ifbrparam param; + u_int16_t pri; + u_int8_t ht, fd, ma, hc, pro; + u_int8_t lladdr[ETHER_ADDR_LEN]; + u_int16_t bprio; + u_int32_t csize, ctime; + u_int32_t ipfflags; + + if (do_cmd(s, BRDGGCACHE, ¶m, sizeof(param), 0) < 0) + return; + csize = param.ifbrp_csize; + if (do_cmd(s, BRDGGTO, ¶m, sizeof(param), 0) < 0) + return; + ctime = param.ifbrp_ctime; + if (do_cmd(s, BRDGGFILT, ¶m, sizeof(param), 0) < 0) + return; + ipfflags = param.ifbrp_filter; + if (do_cmd(s, BRDGPARAM, &ifbp, sizeof(ifbp), 0) < 0) + return; + pri = ifbp.ifbop_priority; + pro = ifbp.ifbop_protocol; + ht = ifbp.ifbop_hellotime; + fd = ifbp.ifbop_fwddelay; + hc = ifbp.ifbop_holdcount; + ma = ifbp.ifbop_maxage; + + printf("\tConfiguration:\n"); + PV2ID(ifbp.ifbop_bridgeid, bprio, lladdr); + printf("\t\tid %s priority %u hellotime %u fwddelay %u\n", + ether_ntoa((struct ether_addr *)lladdr), pri, ht, fd); + printf("\t\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n", + ma, hc, stpproto[pro], csize, ctime); + + PV2ID(ifbp.ifbop_designated_root, bprio, lladdr); + printf("\t\troot id %s priority %d ifcost %u port %u\n", + ether_ntoa((struct ether_addr *)lladdr), bprio, + ifbp.ifbop_root_path_cost, ifbp.ifbop_root_port & 0xfff); + + printf("\t\tipfilter %s flags 0x%x\n", + (ipfflags & IFBF_FILT_USEIPF) ? "enabled" : "disabled", ipfflags); + + bridge_interfaces(s, "\tmember: "); + + if (!all || verbose > 1) { + printf("\tAddress cache:\n"); + bridge_addresses(s, "\t\t"); + bridge_mac_nat(s, "\t\t"); + } + return; + +} + +static void +setbridge_add(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADD, &req, sizeof(req), 1) < 0) + err(1, "BRDGADD %s", val); +} + +static void +setbridge_delete(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDEL, &req, sizeof(req), 1) < 0) + err(1, "BRDGDEL %s", val); +} + +static void +setbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 1); +} + +static void +unsetbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 0); +} + +static void +setbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 1); +} + +static void +unsetbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 0); +} + +#ifdef notdef +static void +setbridge_sticky(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STICKY, 1); +} + +static void +unsetbridge_sticky(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STICKY, 0); +} + +static void +setbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADDS, &req, sizeof(req), 1) < 0) + err(1, "BRDGADDS %s", val); +} + +static void +unsetbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDELS, &req, sizeof(req), 1) < 0) + err(1, "BRDGDELS %s", val); +} +#endif + +static void +setbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 1); +} + +static void +unsetbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 0); +} + +#ifdef notdef +static void +setbridge_edge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_EDGE, 1); +} + +static void +unsetbridge_edge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_EDGE, 0); +} + +static void +setbridge_autoedge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOEDGE, 1); +} + +static void +unsetbridge_autoedge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOEDGE, 0); +} + +static void +setbridge_ptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_PTP, 1); +} + +static void +unsetbridge_ptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_PTP, 0); +} + +static void +setbridge_autoptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOPTP, 1); +} + +static void +unsetbridge_autoptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOPTP, 0); +} +#endif + +static void +setbridge_flush(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHDYN; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_flushall(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHALL; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_static(const char *val, const char *mac, int s, + const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname)); + + ea = ether_aton(mac); + if (ea == NULL) + errx(1, "%s: invalid address: %s", val, mac); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + req.ifba_flags = IFBAF_STATIC; + req.ifba_vlan = 1; /* XXX allow user to specify */ + + if (do_cmd(s, BRDGSADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGSADDR %s", val); +} + +static void +setbridge_deladdr(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + + ea = ether_aton(val); + if (ea == NULL) + errx(1, "invalid address: %s", val); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + + if (do_cmd(s, BRDGDADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGDADDR %s", val); +} + +static void +setbridge_addr(const char *val, int d, int s, const struct afswtch *afp) +{ + + bridge_addresses(s, ""); +} + +static void +setbridge_maxaddr(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_csize = val & 0xffffffff; + + if (do_cmd(s, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSCACHE %s", arg); +} + +static void +setbridge_hellotime(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_hellotime = val & 0xff; + + if (do_cmd(s, BRDGSHT, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSHT %s", arg); +} + +static void +setbridge_fwddelay(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_fwddelay = val & 0xff; + + if (do_cmd(s, BRDGSFD, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSFD %s", arg); +} + +static void +setbridge_maxage(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_maxage = val & 0xff; + + if (do_cmd(s, BRDGSMA, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSMA %s", arg); +} + +static void +setbridge_priority(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_prio = val & 0xffff; + + if (do_cmd(s, BRDGSPRI, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPRI %s", arg); +} + +#ifdef notdef +static void +setbridge_protocol(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + + if (strcasecmp(arg, "stp") == 0) { + param.ifbrp_proto = 0; + } else if (strcasecmp(arg, "rstp") == 0) { + param.ifbrp_proto = 2; + } else { + errx(1, "unknown stp protocol"); + } + + if (do_cmd(s, BRDGSPROTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPROTO %s", arg); +} + +static void +setbridge_holdcount(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_txhc = val & 0xff; + + if (do_cmd(s, BRDGSTXHC, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTXHC %s", arg); +} +#endif + +static void +setbridge_ifpriority(const char *ifn, const char *pri, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", pri); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_priority = val & 0xff; + + if (do_cmd(s, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFPRIO %s", pri); +} + +static void +setbridge_ifpathcost(const char *ifn, const char *cost, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(cost, &val) < 0) + errx(1, "invalid value: %s", cost); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_path_cost = val; + + if (do_cmd(s, BRDGSIFCOST, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFCOST %s", cost); +} + +#ifdef notdef +static void +setbridge_ifmaxaddr(const char *ifn, const char *arg, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_addrmax = val & 0xffffffff; + + if (do_cmd(s, BRDGSIFAMAX, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFAMAX %s", arg); +} +#endif + +static void +setbridge_timeout(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_ctime = val & 0xffffffff; + + if (do_cmd(s, BRDGSTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTO %s", arg); +} + +#ifdef notdef +static void +setbridge_private(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_PRIVATE, 1); +} + +static void +unsetbridge_private(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_PRIVATE, 0); +} +#endif + + +static void +setbridge_hostfilter(const char *ifn, const char *addr, int s, + const struct afswtch *afp) +{ + struct ifbrhostfilter req; + struct ether_addr *ea; + struct in_addr in; + + memset(&req, 0, sizeof(req)); + req.ifbrhf_flags = IFBRHF_ENABLED; + + strlcpy(req.ifbrhf_ifsname, ifn, sizeof(req.ifbrhf_ifsname)); + + ea = ether_aton(addr); + if (ea != NULL) { + req.ifbrhf_flags |= IFBRHF_HWSRC; + bcopy(ea, req.ifbrhf_hwsrca, sizeof(req.ifbrhf_hwsrca)); + } else if (inet_aton(addr, &in) != 0) { + req.ifbrhf_flags |= IFBRHF_IPSRC; + req.ifbrhf_ipsrc = in.s_addr; + } else + errx(1, "invalid address: %s", addr); + + if (do_cmd(s, BRDGSHOSTFILTER, &req, sizeof(req), 1) < 0) + err(1, "BRDGSHOSTFILTER %s %s", ifn, addr); +} + +static void +unsetbridge_hostfilter(const char *ifn, int d, int s, const struct afswtch *afp) +{ + struct ifbrhostfilter req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbrhf_ifsname, ifn, sizeof(req.ifbrhf_ifsname)); + + if (do_cmd(s, BRDGSHOSTFILTER, &req, sizeof(req), 1) < 0) + err(1, "BRDGSHOSTFILTER"); +} + +static void +setbridge_macnat(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_MAC_NAT, 1); +} + +static void +unsetbridge_macnat(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_MAC_NAT, 0); +} + +static struct cmd bridge_cmds[] = { + DEF_CMD_ARG("addm", setbridge_add), + DEF_CMD_ARG("deletem", setbridge_delete), + DEF_CMD_ARG("discover", setbridge_discover), + DEF_CMD_ARG("-discover", unsetbridge_discover), + DEF_CMD_ARG("learn", setbridge_learn), + DEF_CMD_ARG("-learn", unsetbridge_learn), +#ifdef notdef + DEF_CMD_ARG("sticky", setbridge_sticky), + DEF_CMD_ARG("-sticky", unsetbridge_sticky), + DEF_CMD_ARG("span", setbridge_span), + DEF_CMD_ARG("-span", unsetbridge_span), +#endif + DEF_CMD_ARG("stp", setbridge_stp), + DEF_CMD_ARG("-stp", unsetbridge_stp), +#ifdef notdef + DEF_CMD_ARG("edge", setbridge_edge), + DEF_CMD_ARG("-edge", unsetbridge_edge), + DEF_CMD_ARG("autoedge", setbridge_autoedge), + DEF_CMD_ARG("-autoedge", unsetbridge_autoedge), + DEF_CMD_ARG("ptp", setbridge_ptp), + DEF_CMD_ARG("-ptp", unsetbridge_ptp), + DEF_CMD_ARG("autoptp", setbridge_autoptp), + DEF_CMD_ARG("-autoptp", unsetbridge_autoptp), +#endif + DEF_CMD("flush", 0, setbridge_flush), + DEF_CMD("flushall", 0, setbridge_flushall), + DEF_CMD_ARG2("static", setbridge_static), + DEF_CMD_ARG("deladdr", setbridge_deladdr), + DEF_CMD("addr", 1, setbridge_addr), + DEF_CMD_ARG("maxaddr", setbridge_maxaddr), + DEF_CMD_ARG("hellotime", setbridge_hellotime), + DEF_CMD_ARG("fwddelay", setbridge_fwddelay), + DEF_CMD_ARG("maxage", setbridge_maxage), + DEF_CMD_ARG("priority", setbridge_priority), +#ifdef notdef + DEF_CMD_ARG("proto", setbridge_protocol), + DEF_CMD_ARG("holdcnt", setbridge_holdcount), +#endif + DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), + DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), +#ifdef notdef + DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr), +#endif + DEF_CMD_ARG("timeout", setbridge_timeout), +#ifdef notdef + DEF_CMD_ARG("private", setbridge_private), + DEF_CMD_ARG("-private", unsetbridge_private), +#endif + DEF_CMD_ARG2("hostfilter", setbridge_hostfilter), + DEF_CMD_ARG("-hostfilter", unsetbridge_hostfilter), + DEF_CMD_ARG("macnat", setbridge_macnat), + DEF_CMD_ARG("-macnat", unsetbridge_macnat), +}; +static struct afswtch af_bridge = { + .af_name = "af_bridge", + .af_af = AF_UNSPEC, + .af_other_status = bridge_status, +}; + +static __constructor void +bridge_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(bridge_cmds); i++) + cmd_register(&bridge_cmds[i]); + af_register(&af_bridge); +#undef N +} diff --git a/network_cmds/ifconfig.tproj/ifclone.c b/network_cmds/ifconfig.tproj/ifclone.c new file mode 100644 index 0000000..127d10b --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifclone.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +static void +list_cloners(void) +{ + struct if_clonereq ifcr; + char *cp, *buf; + int idx; + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket(AF_INET,SOCK_DGRAM)"); + + memset(&ifcr, 0, sizeof(ifcr)); + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for count"); + + buf = malloc(ifcr.ifcr_total * IFNAMSIZ); + if (buf == NULL) + err(1, "unable to allocate cloner name buffer"); + + ifcr.ifcr_count = ifcr.ifcr_total; + ifcr.ifcr_buffer = buf; + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for names"); + + /* + * In case some disappeared in the mean time, clamp it down. + */ + if (ifcr.ifcr_count > ifcr.ifcr_total) + ifcr.ifcr_count = ifcr.ifcr_total; + + for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) { + if (idx > 0) + putchar(' '); + printf("%s", cp); + } + + putchar('\n'); + free(buf); +} + +static clone_callback_func *clone_cb = NULL; + +void +clone_setcallback(clone_callback_func *p) +{ + if (clone_cb != NULL && clone_cb != p) + errx(1, "conflicting device create parameters"); + clone_cb = p; +} + +/* + * Do the actual clone operation. Any parameters must have been + * setup by now. If a callback has been setup to do the work + * then defer to it; otherwise do a simple create operation with + * no parameters. + */ +static void +ifclonecreate(int s, void *arg) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (clone_cb == NULL) { +#ifdef SIOCIFCREATE2 + /* NB: no parameters */ + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + err(1, "SIOCIFCREATE2"); +#else + if (ioctl(s, SIOCIFCREATE, &ifr) < 0) + err(1, "SIOCIFCREATE"); +#endif + } else { + clone_cb(s, &ifr); + } + + /* + * If we get a different name back than we put in, print it. + */ + if (strncmp(name, ifr.ifr_name, sizeof(name)) != 0) { + strlcpy(name, ifr.ifr_name, sizeof(name)); + printf("%s\n", name); + } +} + +static +DECL_CMD_FUNC(clone_create, arg, d) +{ + callback_register(ifclonecreate, NULL); +} + +static +DECL_CMD_FUNC(clone_destroy, arg, d) +{ + (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + err(1, "SIOCIFDESTROY"); +} + +static struct cmd clone_cmds[] = { + DEF_CLONE_CMD("create", 0, clone_create), + DEF_CMD("destroy", 0, clone_destroy), + DEF_CLONE_CMD("plumb", 0, clone_create), + DEF_CMD("unplumb", 0, clone_destroy), +}; + +static void +clone_Copt_cb(const char *optarg __unused) +{ + list_cloners(); + exit(0); +} +static struct option clone_Copt = { "C", "[-C]", clone_Copt_cb }; + +static __constructor void +clone_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(clone_cmds); i++) + cmd_register(&clone_cmds[i]); + opt_register(&clone_Copt); +#undef N +} diff --git a/network_cmds/ifconfig.tproj/ifconfig.8 b/network_cmds/ifconfig.tproj/ifconfig.8 new file mode 100644 index 0000000..9b06f04 --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifconfig.8 @@ -0,0 +1,1109 @@ +.\" Copyright (c) 2013 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 +.\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.142.2.6.2.1 2008/11/25 02:59:29 kensmith Exp $ +.\" +.Dd June 20, 2008 +.Dt IFCONFIG 8 +.Os +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm +.Op Fl L +.Op Fl m +.Op Fl r +.Ar interface +.Op Cm create +.Op Ar address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm +.Ar interface +.Cm destroy +.Nm +.Fl a +.Op Fl L +.Op Fl d +.Op Fl m +.Op Fl r +.Op Fl u +.Op Fl v +.Op Ar address_family +.Nm +.Fl l +.Op Fl d +.Op Fl u +.Op Ar address_family +.Nm +.Op Fl L +.Op Fl d +.Op Fl m +.Op Fl r +.Op Fl u +.Op Fl v +.Op Fl C +.Nm +.Ar interface +.Cm vlan +.Ar vlan-tag +.Cm vlandev +.Ar iface +.Nm +.Ar interface +.Cm -vlandev +.Ar iface +.Nm +.Ar interface +.Cm bonddev +.Ar iface +.Nm +.Ar interface +.Cm -bonddev +.Ar iface +.Nm +.Ar interface +.Cm bondmode +.Ar lacp | static +.Sh DESCRIPTION +The +.Nm +utility is used to assign an address +to a network interface and/or configure +network interface parameters. +.Pp +The following options are available: +.Bl -tag -width indent +.It Ar address +For the +.Tn DARPA Ns -Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +.Pp +It is also possible to use the CIDR notation (also known as the +slash notation) to include the netmask. +That is, one can specify an address like +.Li 192.168.0.1/16 . +.Pp +For the +.Dq inet6 +family, it is also possible to specify the prefix length using the slash +notation, like +.Li ::1/128 . +See the +.Cm prefixlen +parameter below for more information. +.\" For the Xerox Network Systems(tm) family, +.\" addresses are +.\" .Ar net:a.b.c.d.e.f , +.\" where +.\" .Ar net +.\" is the assigned network number (in decimal), +.\" and each of the six bytes of the host number, +.\" .Ar a +.\" through +.\" .Ar f , +.\" are specified in hexadecimal. +.\" The host number may be omitted on IEEE 802 protocol +.\" (Ethernet, FDDI, and Token Ring) interfaces, +.\" which use the hardware physical address, +.\" and on interfaces other than the first. +.\" For the +.\" .Tn ISO +.\" family, addresses are specified as a long hexadecimal string, +.\" as in the Xerox family. +.\" However, two consecutive dots imply a zero +.\" byte, and the dots are optional, if the user wishes to (carefully) +.\" count out long strings of digits in network byte order. +.Pp +The link-level +.Pq Dq link +address +is specified as a series of colon-separated hex digits. +This can be used to +e.g.\& set a new MAC address on an ethernet interface, though the +mechanism used is not ethernet-specific. +If the interface is already +up when this option is used, it will be briefly brought down and +then brought back up again in order to ensure that the receive +filter in the underlying ethernet hardware is properly reprogrammed. +.It Ar address_family +Specify the +address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommended. +The address or protocol families currently +supported are +.Dq inet , +.Dq inet6 , +.\".Dq atalk , +.\".Dq ipx , +.\" .Dq iso , +and +.Dq link . +.\" and +.\" .Dq ns . +The default is +.Dq inet . +.Dq ether +and +.Dq lladdr +are synonyms for +.Dq link . +.It Ar dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Ar interface +This +parameter is a string of the form +.Dq name unit , +for example, +.Dq Li en0 . +\.El +.Pp +The following parameters may be set with +.Nm : +.Bl -tag -width indent +.It Cm add +Another name for the +.Cm alias +parameter. +Introduced for compatibility +with +.Bsx . +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +If the address is on the same subnet as the first network address +for this interface, a non-conflicting netmask must be given. +Usually +.Li 0xffffffff +is most appropriate. +.It Fl alias +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm anycast +(Inet6 only.) +Specify that the address configured is an anycast address. +Based on the current specification, +only routers may configure anycast addresses. +Anycast address will not be used as source address of any of outgoing +IPv6 packets. +.It Cm arp +Enable the use of the Address Resolution Protocol +.Pq Xr arp 4 +in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and +.Tn IEEE +802 48-bit MAC addresses (Ethernet, FDDI, and Token Ring addresses). +.It Fl arp +Disable the use of the Address Resolution Protocol +.Pq Xr arp 4 . +.It Cm broadcast +(Inet only.) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.It Cm delete +Another name for the +.Fl alias +parameter. +.It Cm down +Mark an interface +.Dq down . +When an interface is marked +.Dq down , +the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +.It Cm ether +Another name for the +.Cm lladdr +parameter. +.\" .It Cm ipdst +.\" This is used to specify an Internet host who is willing to receive +.\" ip packets encapsulating NS packets bound for a remote network. +.\" An apparent point to point link is constructed, and +.\" the address specified will be taken as the NS address and network +.\" of the destination. +.\" IP encapsulation of +.\" .Tn CLNP +.\" packets is done differently. +.It Cm lladdr Ar addr +Set the link-level address on an interface. +This can be used to +e.g. set a new MAC address on an ethernet interface, though the +mechanism used is not ethernet-specific. +The address +.Ar addr +is specified as a series of colon-separated hex digits. +If the interface is already +up when this option is used, it will be briefly brought down and +then brought back up again in order to ensure that the receive +filter in the underlying ethernet hardware is properly reprogrammed. +.It Cm media Ar type +If the driver supports the media selection system, set the media type +of the interface to +.Ar type . +Some interfaces support the mutually exclusive use of one of several +different physical media connectors. +For example, a 10Mbit/s Ethernet +interface might support the use of either +.Tn AUI +or twisted pair connectors. +Setting the media type to +.Cm 10base5/AUI +would change the currently active connector to the AUI port. +Setting it to +.Cm 10baseT/UTP +would activate twisted pair. +Refer to the interfaces' driver +specific documentation or man page for a complete list of the +available types. +.It Cm mediaopt Ar opts +If the driver supports the media selection system, set the specified +media options on the interface. +The +.Ar opts +argument +is a comma delimited list of options to apply to the interface. +Refer to the interfaces' driver specific man page for a complete +list of available options. +.It Fl mediaopt Ar opts +If the driver supports the media selection system, disable the +specified media options on the interface. +.It Cm rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +enable receive (or transmit) checksum offloading on the interface. +Some drivers may not be able to enable these flags independently +of each other, so setting one may also set the other. +The driver will offload as much checksum work as it can reliably +support, the exact level of offloading varies between drivers. +.It Fl rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +disable receive (or transmit) checksum offloading on the interface. +These settings may not always be independent of each other. +.It Cm tso +If the driver supports +.Xr tcp 4 +segmentation offloading, enable TSO on the interface. +Some drivers may not be able to support TSO for +.Xr ip 4 +and +.Xr ip6 4 +packets, so they may enable only one of them. +.It Fl tso +If the driver supports +.Xr tcp 4 +segmentation offloading, disable TSO on the interface. +It will always disable TSO for +.Xr ip 4 +and +.Xr ip6 4 . +.It Cm lro +If the driver supports +.Xr tcp 4 +large receive offloading, enable LRO on the interface. +.It Fl lro +If the driver supports +.Xr tcp 4 +large receive offloading, disable LRO on the interface. +.It Cm av +If supported by the driver, enable 802.1 AVB on the interface. +.It Fl av +If supported by the driver, disable 802.1 AVB on the interface. +.It Cm vlanmtu , vlanhwtag +If the driver offers user-configurable VLAN support, enable +reception of extended frames or tag processing in hardware, +respectively. +Note that this must be issued on a physical interface associated with +.Xr vlan 4 , +not on a +.Xr vlan 4 +interface itself. +.It Fl vlanmtu , vlanhwtag +If the driver offers user-configurable VLAN support, disable +reception of extended frames or tag processing in hardware, +respectively. +.It Cm create +Create the specified network pseudo-device. +If the interface is given without a unit number, try to create a new +device with an arbitrary unit number. +If creation of an arbitrary device is successful, the new device name is +printed to standard output unless the interface is renamed or destroyed +in the same +.Nm +invocation. +.It Cm destroy +Destroy the specified network pseudo-device. +.It Cm plumb +Another name for the +.Cm create +parameter. +Included for +.Tn Solaris +compatibility. +.It Cm unplumb +Another name for the +.Cm destroy +parameter. +Included for +.Tn Solaris +compatibility. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as additional hops +to the destination network or host. +.It Cm mtu Ar n +Set the maximum transmission unit of the interface to +.Ar n , +default is interface specific. +The MTU is used to limit the size of packets that are transmitted on an +interface. +Not all interfaces support setting the MTU, and some interfaces have +range restrictions. +.It Cm netmask Ar mask +.\" (Inet and ISO.) +(Inet only.) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading +.Ql 0x , +with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.Pp +The netmask can also be specified in CIDR notation after the address. +See the +.Ar address +option above for more information. +.It Cm prefixlen Ar len +(Inet6 only.) +Specify that +.Ar len +bits are reserved for subdividing networks into sub-networks. +The +.Ar len +must be integer, and for syntactical reason it must be between 0 to 128. +It is almost always 64 under the current IPv6 assignment rule. +If the parameter is omitted, 64 is used. +.Pp +The prefix can also be specified using the slash notation after the address. +See the +.Ar address +option above for more information. +.\" see +.\" Xr eon 5 . +.\" .It Cm nsellength Ar n +.\" .Pf ( Tn ISO +.\" only) +.\" This specifies a trailing number of bytes for a received +.\" .Tn NSAP +.\" used for local identification, the remaining leading part of which is +.\" taken to be the +.\" .Tn NET +.\" (Network Entity Title). +.\" The default value is 1, which is conformant to US +.\" .Tn GOSIP . +.\" When an ISO address is set in an ifconfig command, +.\" it is really the +.\" .Tn NSAP +.\" which is being specified. +.\" For example, in +.\" .Tn US GOSIP , +.\" 20 hex digits should be +.\" specified in the +.\" .Tn ISO NSAP +.\" to be assigned to the interface. +.\" There is some evidence that a number different from 1 may be useful +.\" for +.\" .Tn AFI +.\" 37 type addresses. +.It Cm remove +Another name for the +.Fl alias +parameter. +Introduced for compatibility +with +.Bsx . +.Sm off +.It Cm link Op Cm 0 No - Cm 2 +.Sm on +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. +An example +of this is to enable SLIP compression, or to select the connector type +for some Ethernet cards. +Refer to the man page for the specific driver +for more information. +.Sm off +.It Fl link Op Cm 0 No - Cm 2 +.Sm on +Disable special processing at the link level with the specified interface. +.It Cm up +Mark an interface +.Dq up . +This may be used to enable an interface after an +.Dq Nm Cm down . +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +The following parameters are for ICMPv6 Neighbor Discovery Protocol. +Note that the address family keyword +.Dq Li inet6 +is needed for them: +.Bl -tag -width indent +.It Cm nud +Perform network unreachability detection (NUD). +.It Cm -nud +Do not perform network unreachability detection (NUD). +.It Cm ifdisabled +Disable all IPv6 communication on the interface. +.It Cm -ifdisabled +Do not disable all IPv6 communication on the interface. +.It Cm insecure +Disable the processing of Secure Neighbor Discovery (SEND). +.It Cm -insecure +Do not disabled the processing of Secure Neighbor Discovery (SEND). +.It Cm dad +Perform duplicate address detection (DAD). +.It Cm -dad +Do not perform duplicate address detection (DAD). +.It Cm replicated +Modify duplicate address detection (DAD) protocol to expect that interface +configuration is replicated at a network sleep proxy. Ignores certain NA +messages and disables optimistic DAD. +.It Cm -replicated +Do not use modified duplicated address detection (DAD) protocol. +.El +.Pp +The following parameters are specific to link aggregate interfaces: +.Bl -tag -width indent +.It Cm bonddev Ar iface +If the interface is a bond pseudo device, associate physical interface +.Ar iface +with it. By default, the bond pseudo device is in LACP +(Link Aggregation Control Protocol) mode (see \fBbondmode\fR below). In +this mode, the device conforms to the IEEE 802.3ad Link Aggregation +specification. +.Pp +If this is the first physical interface to be associated with the bond +interface, the bond interface inherits the ethernet address from the +physical interface. Physical interfaces that are added to the bond have +their ethernet address re-programmed so that all members of the bond have +the same ethernet address. If the physical interface is subsequently +removed from the bond using +.Fl bonddev , +a new ethernet address is chosen from the remaining interfaces, and all +interfaces are re-programmed again with the new ethernet address. If no +remaining interfaces exist, the bond interface's ethernet address is cleared. +.Pp +If the specified physical interface +.Ar iface +is not capable of having its ethernet address re-programmed, the +.Cm bonddev +command will fail. +.Pp +Once the physical interface +.Ar iface +is successfully associated with the bond interface, all received packets +are diverted to the bond interface. The physical interface is no longer +useable on its own, and remains that way until it is removed from the bond using +.Fl bonddev . +.Pp +It is possible that the specified interface +.Ar iface +is not capable of aggregating, and may remain unused until the operating +conditions change. +.Pp +The link status of the bond interface depends on the state of link aggregation. +If no active partner is detected, the link status will remain inactive. +.Pp +To monitor the 802.3ad Link Aggregation state, use the +.Fl b +option. +.Pp +A physical interface that is associated with a vlan pseudo device cannot +at the same time be associated with a bond pseudo device. A physical interface +cannot be associated with more than one bond pseudo device at the same time. +.Pp +It is not possible to associate a bond with pseudo interfaces such as vlan. +Only physical ethernet interfaces may be associated with a bond. +.It Fl bonddev Ar iface +If the interface is a bond pseudo device, disassociate the physical interface +.Ar iface +from it. Before the interface is removed from the bond, the bond device +announces to the link partner that the interface is now individual and +no longer aggregatable. +If the physical +.Ar iface +is the last interface in the bond, the bond interface clears its link address. +.It Cm bondmode Ar lacp | static +If the interface is a bond pseudo device, this option will set the \fImode\fR +on the bond interface. The two currently supported modes are +.Ar lacp +and +.Ar static . +The default mode is +.Ar lacp . +.Pp +To enable static mode (and turn off LACP), specify +.Ar static . +In static mode, a member interface is made an active part of the +link aggregate as long as the link status is active. +.Pp +To re-enable LACP mode, specify +.Ar lacp . +.El +.Pp +The following parameters are specific to IP tunnel interfaces, +.Xr gif 4 : +.Bl -tag -width indent +.It Cm tunnel Ar src_addr dest_addr +Configure the physical source and destination address for IP tunnel +interfaces. +The arguments +.Ar src_addr +and +.Ar dest_addr +are interpreted as the outer source/destination for the encapsulating +IPv4/IPv6 header. +.It Fl tunnel +Unconfigure the physical source and destination address for IP tunnel +interfaces previously configured with +.Cm tunnel . +.It Cm deletetunnel +Another name for the +.Fl tunnel +parameter. +.El +.Pp +The following parameters are specific to bridge interfaces: +.Bl -tag -width indent +.It Cm addm Ar interface +Add the interface named by +.Ar interface +as a member of the bridge. +The interface is put into promiscuous mode +so that it can receive every packet sent on the network. +.It Cm deletem Ar interface +Remove the interface named by +.Ar interface +from the bridge. +Promiscuous mode is disabled on the interface when +it is removed from the bridge. +.It Cm maxaddr Ar size +Set the size of the bridge address cache to +.Ar size . +The default is 100 entries. +.It Cm timeout Ar seconds +Set the timeout of address cache entries to +.Ar seconds +seconds. +If +.Ar seconds +is zero, then address cache entries will not be expired. +The default is 240 seconds. +.It Cm addr +Display the addresses that have been learned by the bridge. +.It Cm static Ar interface-name Ar address +Add a static entry into the address cache pointing to +.Ar interface-name . +Static entries are never aged out of the cache or re-placed, even if the +address is seen on a different interface. +.It Cm deladdr Ar address +Delete +.Ar address +from the address cache. +.It Cm flush +Delete all dynamically-learned addresses from the address cache. +.It Cm flushall +Delete all addresses, including static addresses, from the address cache. +.It Cm discover Ar interface +Mark an interface as a +.Dq discovering +interface. +When the bridge has no address cache entry +(either dynamic or static) +for the destination address of a packet, +the bridge will forward the packet to all +member interfaces marked as +.Dq discovering . +This is the default for all interfaces added to a bridge. +.It Cm -discover Ar interface +Clear the +.Dq discovering +attribute on a member interface. +For packets without the +.Dq discovering +attribute, the only packets forwarded on the interface are broadcast +or multicast packets and packets for which the destination address +is known to be on the interface's segment. +.It Cm learn Ar interface +Mark an interface as a +.Dq learning +interface. +When a packet arrives on such an interface, the source +address of the packet is entered into the address cache as being a +destination address on the interface's segment. +This is the default for all interfaces added to a bridge. +.It Cm -learn Ar interface +Clear the +.Dq learning +attribute on a member interface. +.\".It Cm sticky Ar interface +.\"Mark an interface as a +.\".Dq sticky +.\"interface. +.\"Dynamically learned address entries are treated at static once entered into +.\"the cache. +.\"Sticky entries are never aged out of the cache or replaced, even if the +.\"address is seen on a different interface. +.\".It Cm -sticky Ar interface +.\"Clear the +.\".Dq sticky +.\"attribute on a member interface. +.\".It Cm private Ar interface +.\"Mark an interface as a +.\".Dq private +.\"interface. +.\"A private interface does not forward any traffic to any other port that is also +.\"a private interface. +.\".It Cm -private Ar interface +.\"Clear the +.\".Dq private +.\"attribute on a member interface. +.\".It Cm span Ar interface +.\"Add the interface named by +.\".Ar interface +.\"as a span port on the bridge. +.\"Span ports transmit a copy of every frame received by the bridge. +.\"This is most useful for snooping a bridged network passively on +.\"another host connected to one of the span ports of the bridge. +.\".It Cm -span Ar interface +.\"Delete the interface named by +.\".Ar interface +.\"from the list of span ports of the bridge. +.It Cm stp Ar interface +Enable Spanning Tree protocol on +.Ar interface . +The +.Xr if_bridge 4 +driver has support for the IEEE 802.1D Spanning Tree protocol (STP). +Spanning Tree is used to detect and remove loops in a network topology. +.It Cm -stp Ar interface +Disable Spanning Tree protocol on +.Ar interface . +This is the default for all interfaces added to a bridge. +.\".It Cm edge Ar interface +.\"Set +.\".Ar interface +.\"as an edge port. +.\"An edge port connects directly to end stations cannot create bridging +.\"loops in the network, this allows it to transition straight to forwarding. +.\".It Cm -edge Ar interface +.\"Disable edge status on +.\".Ar interface . +.\".It Cm autoedge Ar interface +.\"Allow +.\".Ar interface +.\"to automatically detect edge status. +.\"This is the default for all interfaces added to a bridge. +.\".It Cm -autoedge Ar interface +.\"Disable automatic edge status on +.\".Ar interface . +.\".It Cm ptp Ar interface +.\"Set the +.\".Ar interface +.\"as a point to point link. +.\"This is required for straight transitions to forwarding and +.\"should be enabled on a direct link to another RSTP capable switch. +.\".It Cm -ptp Ar interface +.\"Disable point to point link status on +.\".Ar interface . +.\"This should be disabled for a half duplex link and for an interface +.\"connected to a shared network segment, +.\"like a hub or a wireless network. +.\".It Cm autoptp Ar interface +.\"Automatically detect the point to point status on +.\".Ar interface +.\"by checking the full duplex link status. +.\"This is the default for interfaces added to the bridge. +.\".It Cm -autoptp Ar interface +.\"Disable automatic point to point link detection on +.\".Ar interface . +.It Cm maxage Ar seconds +Set the time that a Spanning Tree protocol configuration is valid. +The default is 20 seconds. +The minimum is 6 seconds and the maximum is 40 seconds. +.It Cm fwddelay Ar seconds +Set the time that must pass before an interface begins forwarding +packets when Spanning Tree is enabled. +The default is 15 seconds. +The minimum is 4 seconds and the maximum is 30 seconds. +.It Cm hellotime Ar seconds +Set the time between broadcasting of Spanning Tree protocol +configuration messages. +The hello time may only be changed when operating in legacy stp mode. +The default is 2 seconds. +The minimum is 1 second and the maximum is 2 seconds. +.It Cm priority Ar value +Set the bridge priority for Spanning Tree. +The default is 32768. +The minimum is 0 and the maximum is 61440. +.\".It Cm proto Ar value +.\"Set the Spanning Tree protocol. +.\"The default is rstp. +.\"The available options are stp and rstp. +.\".It Cm holdcnt Ar value +.\"Set the transmit hold count for Spanning Tree. +.\"This is the number of packets transmitted before being rate limited. +.\"The default is 6. +.\"The minimum is 1 and the maximum is 10. +.It Cm ifpriority Ar interface Ar value +Set the Spanning Tree priority of +.Ar interface +to +.Ar value . +The default is 128. +The minimum is 0 and the maximum is 240. +.It Cm ifpathcost Ar interface Ar value +Set the Spanning Tree path cost of +.Ar interface +to +.Ar value . +The default is calculated from the link speed. +To change a previously selected path cost back to automatic, set the +cost to 0. +The minimum is 1 and the maximum is 200000000. +.It Cm ifmaxaddr Ar interface Ar size +Set the maximum number of hosts allowed from an interface, packets with unknown +source addresses are dropped until an existing host cache entry expires or is +removed. +Set to 0 to disable. +.It Cm hostfilter Ar interface Ar address +Configure the bridge to accept incoming packet on the interface +only if they match the given MAC address and IP address +-- use the command twice to set both type of addresses. +Other filtering restrictions apply. +.It Cm -hostfilter Ar interface +Allow traffic from any host on that interface. +.El +.Pp +The following parameters are specific to vlan interfaces: +.Bl -tag -width indent +.It Cm vlan Ar vlan_tag +Set the VLAN tag value to +.Ar vlan_tag . +This value is a 16-bit number which is used to create an 802.1Q +VLAN header for packets sent from the +.Xr vlan 4 +interface. +Note that +.Cm vlan +and +.Cm vlandev +must both be set at the same time. +.It Cm vlandev Ar iface +Associate the physical interface +.Ar iface +with a +.Xr vlan 4 +interface. +Packets transmitted through the +.Xr vlan 4 +interface will be +diverted to the specified physical interface +.Ar iface +with 802.1Q VLAN encapsulation. +Packets with 802.1Q encapsulation received +by the parent interface with the correct VLAN tag will be diverted to +the associated +.Xr vlan 4 +pseudo-interface. +The +.Xr vlan 4 +interface is assigned a +copy of the parent interface's flags and the parent's ethernet address. +The +.Cm vlandev +and +.Cm vlan +must both be set at the same time. +If the +.Xr vlan 4 +interface already has +a physical interface associated with it, this command will fail. +To +change the association to another physical interface, the existing +association must be cleared first. +.Pp +Note: if the hardware tagging capability +is set on the parent interface, the +.Xr vlan 4 +pseudo +interface's behavior changes: +the +.Xr vlan 4 +interface recognizes that the +parent interface supports insertion and extraction of VLAN tags on its +own (usually in firmware) and that it should pass packets to and from +the parent unaltered. +.It Fl vlandev Op Ar iface +If the driver is a +.Xr vlan 4 +pseudo device, disassociate the parent interface from it. +This breaks the link between the +.Xr vlan 4 +interface and its parent, +clears its VLAN tag, flags and its link address and shuts the interface down. +The +.Ar iface +argument is useless and hence deprecated. +.El +.Pp +The +.Nm +utility displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +.Nm +will report only the details specific to that protocol family. +.Pp +If the +.Fl m +flag is passed before an interface name, +.Nm +will display the capability list and all +of the supported media for the specified interface. +.Pp +If +.Fl L +flag is supplied, address lifetime is displayed for IPv6 addresses, +as time offset string. +.Pp +Optionally, the +.Fl a +flag may be used instead of an interface name. +This flag instructs +.Nm +to display information about all interfaces in the system. +The +.Fl d +flag limits this to interfaces that are down, and +.Fl u +limits this to interfaces that are up. +When no arguments are given, +.Fl a +is implied. +.Pp +The +.Fl l +flag may be used to list all available interfaces on the system, with +no other additional information. +Use of this flag is mutually exclusive +with all other flags and commands, except for +.Fl d +(only list interfaces that are down) +and +.Fl u +(only list interfaces that are up). +.Pp +The +.Fl v +flag may be used to get more verbose status for an interface. +.Pp +The +.Fl C +flag may be used to list all of the interface cloners available on +the system, with no additional information. +Use of this flag is mutually exclusive with all other flags and commands. +.Pp +The +.Fl r +flag may be used to show additional information related to the count of route references on the network interface. +.Pp +For bridge interfaces, the list of addresses learned by the bridge is not shown when displaying information about +all interfaces except when the +.Fl v +flag is used. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh NOTES +The media selection system is relatively new and only some drivers support +it (or have need for it). +.Sh EXAMPLES +Assign the IPv4 address +.Li 192.0.2.10 , +with a network mask of +.Li 255.255.255.0 , +to the interface +.Li en0 : +.Dl # ifconfig en0 inet 192.0.2.10 netmask 255.255.255.0 +.Pp +Add the IPv4 address +.Li 192.0.2.45 , +with the CIDR network prefix +.Li /28 , +to the interface +.Li en0 , +using +.Cm add +as a synonym for the canonical form of the option +.Cm alias : +.Dl # ifconfig en0 inet 192.0.2.45/28 add +.Pp +Remove the IPv4 address +.Li 192.0.2.45 +from the interface +.Li en0 : +.Dl # ifconfig en0 inet 192.0.2.45 -alias +.Pp +Add the IPv6 address +.Li 2001:DB8:DBDB::123/48 +to the interface +.Li en0 : +.Dl # ifconfig en0 inet6 2001:db8:bdbd::123 prefixlen 48 alias +Note that lower case hexadecimal IPv6 addresses are acceptable. +.Pp +Remove the IPv6 address added in the above example, +using the +.Li / +character as shorthand for the network prefix, +and using +.Cm delete +as a synonym for the canonical form of the option +.Fl alias : +.Dl # ifconfig en0 inet6 2001:db8:bdbd::123/48 delete +.Pp +Configure the interface +.Li en1 , +to use 100baseTX, full duplex Ethernet media options: +.Dl # ifconfig en1 media 100baseTX mediaopt full-duplex +.Pp +Create the software network interface +.Li gif1 : +.Dl # ifconfig gif1 create +.Pp +Destroy the software network interface +.Li gif1 : +.Dl # ifconfig gif1 destroy +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exist, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . +.Sh BUGS +Basic IPv6 node operation requires a link-local address on each +interface configured for IPv6. +Normally, such an address is automatically configured by the +kernel on each interface added to the system; this behaviour may +be disabled by setting the sysctl MIB variable +.Va net.inet6.ip6.auto_linklocal +to 0. +.Pp +If you delete such an address using +.Nm , +the kernel may act very odd. +Do this at your own risk. diff --git a/network_cmds/ifconfig.tproj/ifconfig.c b/network_cmds/ifconfig.tproj/ifconfig.c new file mode 100644 index 0000000..3449301 --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifconfig.c @@ -0,0 +1,2692 @@ +/* + * Copyright (c) 2009-2019 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#ifndef __APPLE__ +#include <sys/module.h> +#include <sys/linker.h> +#endif + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_mib.h> +#include <net/route.h> +#include <net/pktsched/pktsched.h> +#include <net/network_agent.h> + +/* IP */ +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <ifaddrs.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <sysexits.h> +#include <syslog.h> + +#include "ifconfig.h" + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +/* + * Since "struct ifreq" is composed of various union members, callers + * should pay special attention to interprete the value. + * (.e.g. little/big endian difference in the structure.) + */ +struct ifreq ifr; + +char name[IFNAMSIZ]; +int setaddr; +int setmask; +int doalias; +int clearaddr; +int newaddr = 1; +int noload; +int all; + +int bond_details = 0; +int supmedia = 0; +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +int verbose = 1; +int showrtref = 1; +#else /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ +int verbose = 0; +int showrtref = 0; +#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ +int printkeys = 0; /* Print keying material for interfaces. */ + +static int ifconfig(int argc, char *const *argv, int iscreate, + const struct afswtch *afp); +static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, + struct ifaddrs *ifa); +static char *bytes_to_str(unsigned long long bytes); +static char *bps_to_str(unsigned long long rate); +static char *ns_to_str(unsigned long long nsec); +static void tunnel_status(int s); +static void clat46_addr(int s, char *name); +static void nat64_status(int s, char *name); +static void usage(void); +static char *sched2str(unsigned int s); +static char *tl2str(unsigned int s); +static char *ift2str(unsigned int t, unsigned int f, unsigned int sf); +static char *iffunct2str(u_int32_t functional_type); + +static struct afswtch *af_getbyname(const char *name); +static struct afswtch *af_getbyfamily(int af); +static void af_other_status(int); + +static struct option *opts = NULL; + +void +opt_register(struct option *p) +{ + p->next = opts; + opts = p; +} + +static void +usage(void) +{ + char options[1024]; + struct option *p; + + /* XXX not right but close enough for now */ + options[0] = '\0'; + for (p = opts; p != NULL; p = p->next) { + strlcat(options, p->opt_usage, sizeof(options)); + strlcat(options, " ", sizeof(options)); + } + + fprintf(stderr, + "usage: ifconfig %sinterface address_family [address [dest_address]]\n" + " [parameters]\n" + " ifconfig interface create\n" + " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n" + " ifconfig -l [-d] [-u] [address_family]\n" + " ifconfig %s[-d] [-m] [-u] [-v]\n", + options, options, options); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int c, namesonly, downonly, uponly; + const struct afswtch *afp = NULL; + int ifindex; + struct ifaddrs *ifap, *ifa; + struct ifreq paifr; + const struct sockaddr_dl *sdl; + char options[1024], *cp; + const char *ifname; + struct option *p; + size_t iflen; + + all = downonly = uponly = namesonly = noload = 0; + + /* Parse leading line options */ +#ifndef __APPLE__ + strlcpy(options, "adklmnuv", sizeof(options)); +#else + strlcpy(options, "abdlmruv", sizeof(options)); +#endif + for (p = opts; p != NULL; p = p->next) + strlcat(options, p->opt, sizeof(options)); + while ((c = getopt(argc, argv, options)) != -1) { + switch (c) { + case 'a': /* scan all interfaces */ + all++; + break; + case 'b': /* bond detailed output */ + bond_details++; + break; + case 'd': /* restrict scan to "down" interfaces */ + downonly++; + break; +#ifndef __APPLE__ + case 'k': + printkeys++; + break; +#endif + case 'l': /* scan interface names only */ + namesonly++; + break; + case 'm': /* show media choices in status */ + supmedia = 1; + break; +#ifndef __APPLE__ + case 'n': /* suppress module loading */ + noload++; + break; +#endif + case 'r': + showrtref++; + break; + case 'u': /* restrict scan to "up" interfaces */ + uponly++; + break; + case 'v': + verbose++; + break; + default: + for (p = opts; p != NULL; p = p->next) + if (p->opt[0] == c) { + p->cb(optarg); + break; + } + if (p == NULL) + usage(); + break; + } + } + argc -= optind; + argv += optind; + + /* -l cannot be used with -a or -q or -m or -b */ + if (namesonly && + (all || supmedia || bond_details)) + usage(); + + /* nonsense.. */ + if (uponly && downonly) + usage(); + + /* no arguments is equivalent to '-a' */ + if (!namesonly && argc < 1) + all = 1; + + /* -a and -l allow an address family arg to limit the output */ + if (all || namesonly) { + if (argc > 1) + usage(); + + ifname = NULL; + if (argc == 1) { + afp = af_getbyname(*argv); + if (afp == NULL) + usage(); + if (afp->af_name != NULL) + argc--, argv++; + /* leave with afp non-zero */ + } + } else { + /* not listing, need an argument */ + if (argc < 1) + usage(); + + ifname = *argv; + argc--, argv++; + +#ifdef notdef + /* check and maybe load support for this interface */ + ifmaybeload(ifname); +#endif + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + /* + * NOTE: We must special-case the `create' command + * right here as we would otherwise fail when trying + * to find the interface. + */ + if (argc > 0 && (strcmp(argv[0], "create") == 0 || + strcmp(argv[0], "plumb") == 0)) { + iflen = strlcpy(name, ifname, sizeof(name)); + if (iflen >= sizeof(name)) + errx(1, "%s: cloning name too long", + ifname); + ifconfig(argc, argv, 1, NULL); + exit(0); + } + errx(1, "interface %s does not exist", ifname); + } + } + + /* Check for address family */ + if (argc > 0) { + afp = af_getbyname(*argv); + if (afp != NULL) + argc--, argv++; + } + + if (getifaddrs(&ifap) != 0) + err(EXIT_FAILURE, "getifaddrs"); + cp = NULL; + ifindex = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + memset(&paifr, 0, sizeof(paifr)); + strlcpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); + if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { + memcpy(&paifr.ifr_addr, ifa->ifa_addr, + ifa->ifa_addr->sa_len); + } + + if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0) + continue; + if (ifa->ifa_addr->sa_family == AF_LINK) + sdl = (const struct sockaddr_dl *) ifa->ifa_addr; + else + sdl = NULL; + if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0) + continue; + iflen = strlcpy(name, ifa->ifa_name, sizeof(name)); + if (iflen >= sizeof(name)) { + warnx("%s: interface name too long, skipping", + ifa->ifa_name); + continue; + } + cp = ifa->ifa_name; + + if (downonly && (ifa->ifa_flags & IFF_UP) != 0) + continue; + if (uponly && (ifa->ifa_flags & IFF_UP) == 0) + continue; + ifindex++; + /* + * Are we just listing the interfaces? + */ + if (namesonly) { + if (ifindex > 1) + printf(" "); + fputs(name, stdout); + continue; + } + + if (argc > 0) + ifconfig(argc, argv, 0, afp); + else + status(afp, sdl, ifa); + } + if (namesonly) + printf("\n"); + freeifaddrs(ifap); + + exit(0); +} + +static struct afswtch *afs = NULL; + +void +af_register(struct afswtch *p) +{ + p->af_next = afs; + afs = p; +} + +static struct afswtch * +af_getbyname(const char *name) +{ + struct afswtch *afp; + + for (afp = afs; afp != NULL; afp = afp->af_next) + if (strcmp(afp->af_name, name) == 0) + return afp; + return NULL; +} + +static struct afswtch * +af_getbyfamily(int af) +{ + struct afswtch *afp; + + for (afp = afs; afp != NULL; afp = afp->af_next) + if (afp->af_af == af) + return afp; + return NULL; +} + +static void +af_other_status(int s) +{ + struct afswtch *afp; + uint8_t afmask[howmany(AF_MAX, NBBY)]; + + memset(afmask, 0, sizeof(afmask)); + for (afp = afs; afp != NULL; afp = afp->af_next) { + if (afp->af_other_status == NULL) + continue; + if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) + continue; + afp->af_other_status(s); + setbit(afmask, afp->af_af); + } +} + +static void +af_all_tunnel_status(int s) +{ + struct afswtch *afp; + uint8_t afmask[howmany(AF_MAX, NBBY)]; + + memset(afmask, 0, sizeof(afmask)); + for (afp = afs; afp != NULL; afp = afp->af_next) { + if (afp->af_status_tunnel == NULL) + continue; + if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) + continue; + afp->af_status_tunnel(s); + setbit(afmask, afp->af_af); + } +} + +static struct cmd *cmds = NULL; + +void +cmd_register(struct cmd *p) +{ + p->c_next = cmds; + cmds = p; +} + +static const struct cmd * +cmd_lookup(const char *name) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + const struct cmd *p; + + for (p = cmds; p != NULL; p = p->c_next) + if (strcmp(name, p->c_name) == 0) + return p; + return NULL; +#undef N +} + +struct callback { + callback_func *cb_func; + void *cb_arg; + struct callback *cb_next; +}; +static struct callback *callbacks = NULL; + +void +callback_register(callback_func *func, void *arg) +{ + struct callback *cb; + + cb = malloc(sizeof(struct callback)); + if (cb == NULL) + errx(1, "unable to allocate memory for callback"); + cb->cb_func = func; + cb->cb_arg = arg; + cb->cb_next = callbacks; + callbacks = cb; +} + +/* specially-handled commands */ +static void setifaddr(const char *, int, int, const struct afswtch *); +static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); + +static void setifdstaddr(const char *, int, int, const struct afswtch *); +static const struct cmd setifdstaddr_cmd = + DEF_CMD("ifdstaddr", 0, setifdstaddr); + +static int +ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp) +{ + const struct afswtch *nafp; + struct callback *cb; + int ret, s; + + strlcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); +top: + if (afp == NULL) + afp = af_getbyname("inet"); + ifr.ifr_addr.sa_family = + afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? + AF_INET : afp->af_af; + + if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) + err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); + + while (argc > 0) { + const struct cmd *p; + + p = cmd_lookup(*argv); + if (p == NULL) { + /* + * Not a recognized command, choose between setting + * the interface address and the dst address. + */ + p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); + } + if (p->c_u.c_func || p->c_u.c_func2) { + if (iscreate && !p->c_iscloneop) { + /* + * Push the clone create callback so the new + * device is created and can be used for any + * remaining arguments. + */ + cb = callbacks; + if (cb == NULL) + errx(1, "internal error, no callback"); + callbacks = cb->cb_next; + cb->cb_func(s, cb->cb_arg); + iscreate = 0; + /* + * Handle any address family spec that + * immediately follows and potentially + * recreate the socket. + */ + nafp = af_getbyname(*argv); + if (nafp != NULL) { + argc--, argv++; + if (nafp != afp) { + close(s); + afp = nafp; + goto top; + } + } + } + if (p->c_parameter == NEXTARG) { + if (argv[1] == NULL) + errx(1, "'%s' requires argument", + p->c_name); + p->c_u.c_func(argv[1], 0, s, afp); + argc--, argv++; + } else if (p->c_parameter == OPTARG) { + p->c_u.c_func(argv[1], 0, s, afp); + if (argv[1] != NULL) + argc--, argv++; + } else if (p->c_parameter == NEXTARG2) { + if (argc < 3) + errx(1, "'%s' requires 2 arguments", + p->c_name); + p->c_u.c_func2(argv[1], argv[2], s, afp); + argc -= 2, argv += 2; + } else if (p->c_parameter == VAARGS) { + ret = p->c_u.c_funcv(argc - 1, argv + 1, s, afp); + if (ret < 0) + errx(1, "'%s' command error", + p->c_name); + argc -= ret, argv += ret; + } else { + p->c_u.c_func(*argv, p->c_parameter, s, afp); + } + } + argc--, argv++; + } + + /* + * Do any post argument processing required by the address family. + */ + if (afp->af_postproc != NULL) + afp->af_postproc(s, afp); + /* + * Do deferred callbacks registered while processing + * command-line arguments. + */ + for (cb = callbacks; cb != NULL; cb = cb->cb_next) + cb->cb_func(s, cb->cb_arg); + /* + * Do deferred operations. + */ + if (clearaddr) { + if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { + warnx("interface %s cannot change %s addresses!", + name, afp->af_name); + clearaddr = 0; + } + } + if (clearaddr) { + strlcpy(afp->af_ridreq, name, sizeof ifr.ifr_name); + ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); + if (ret < 0) { + if (errno == EADDRNOTAVAIL && (doalias >= 0)) { + /* means no previous address for interface */ + } else + Perror("ioctl (SIOCDIFADDR)"); + } + } + if (newaddr) { + if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { + warnx("interface %s cannot change %s addresses!", + name, afp->af_name); + newaddr = 0; + } + } + if (newaddr && (setaddr || setmask)) { + strlcpy(afp->af_addreq, name, sizeof ifr.ifr_name); + if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) + Perror("ioctl (SIOCAIFADDR)"); + } + + close(s); + return(0); +} + +/*ARGSUSED*/ +static void +setifaddr(const char *addr, int param, int s, const struct afswtch *afp) +{ + if (afp->af_getaddr == NULL) + return; + /* + * Delay the ioctl to set the interface addr until flags are all set. + * The address interpretation may depend on the flags, + * and the flags may change when the address is set. + */ + setaddr++; + if (doalias == 0 && afp->af_af != AF_LINK) + clearaddr = 1; + afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); +} + +static void +settunnel(const char *src, const char *dst, int s, const struct afswtch *afp) +{ + struct addrinfo *srcres, *dstres; + int ecode; + + if (afp->af_settunnel == NULL) { + warn("address family %s does not support tunnel setup", + afp->af_name); + return; + } + + if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(ecode)); + + if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(ecode)); + + if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) + errx(1, + "source and destination address families do not match"); + + afp->af_settunnel(s, srcres, dstres); + + freeaddrinfo(srcres); + freeaddrinfo(dstres); +} + +/* ARGSUSED */ +static void +deletetunnel(const char *vname, int param, int s, const struct afswtch *afp) +{ + + if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) + err(1, "SIOCDIFPHYADDR"); +} + +static void +setifnetmask(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getaddr != NULL) { + setmask++; + afp->af_getaddr(addr, MASK); + } +} + +static void +setifbroadaddr(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getaddr != NULL) + afp->af_getaddr(addr, DSTADDR); +} + +static void +setifipdst(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + const struct afswtch *inet; + + inet = af_getbyname("inet"); + if (inet == NULL) + return; + inet->af_getaddr(addr, DSTADDR); + clearaddr = 0; + newaddr = 0; +} + +static void +notealias(const char *addr, int param, int s, const struct afswtch *afp) +{ +#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) + if (setaddr && doalias == 0 && param < 0) + if (afp->af_addreq != NULL && afp->af_ridreq != NULL) + bcopy((caddr_t)rqtosa(af_addreq), + (caddr_t)rqtosa(af_ridreq), + rqtosa(af_addreq)->sa_len); + doalias = param; + if (param < 0) { + clearaddr = 1; + newaddr = 0; + } else + clearaddr = 0; +#undef rqtosa +} + +/*ARGSUSED*/ +static void +setifdstaddr(const char *addr, int param __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getaddr != NULL) + afp->af_getaddr(addr, DSTADDR); +} + +/* + * Note: doing an SIOCIGIFFLAGS scribbles on the union portion + * of the ifreq structure, which may confuse other parts of ifconfig. + * Make a private copy so we can avoid that. + */ +static void +setifflags(const char *vname, int value, int s, const struct afswtch *afp) +{ + struct ifreq my_ifr; + int flags; + + bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq)); + + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + strlcpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name)); + flags = my_ifr.ifr_flags; + + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + my_ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) + Perror(vname); +} + +void +setifcap(const char *vname, int value, int s, const struct afswtch *afp) +{ + int flags; + + if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFCAP)"); + exit(1); + } + flags = ifr.ifr_curcap; + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + flags &= ifr.ifr_reqcap; + ifr.ifr_reqcap = flags; + if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) + Perror(vname); +} + +static void +setifmetric(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_metric = atoi(val); + if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) + warn("ioctl (set metric)"); +} + +static void +setifmtu(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_mtu = atoi(val); + if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) + warn("ioctl (set mtu)"); +} + +#ifndef __APPLE__ +static void +setifname(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + char *newname; + + newname = strdup(val); + if (newname == NULL) { + warn("no memory to set ifname"); + return; + } + ifr.ifr_data = newname; + if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { + warn("ioctl (set name)"); + free(newname); + return; + } + strlcpy(name, newname, sizeof(name)); + free(newname); +} +#endif + +static void +setrouter(const char *vname, int value, int s, const struct afswtch *afp) +{ + if (afp->af_setrouter == NULL) { + warn("address family %s does not support router mode", + afp->af_name); + return; + } + + afp->af_setrouter(s, value); +} + +static int +routermode(int argc, char *const *argv, int s, const struct afswtch *afp) +{ + return (*afp->af_routermode)(s, argc, argv); +} + + +static void +setifdesc(const char *val, int dummy __unused, int s, const struct afswtch *afp) +{ + struct if_descreq ifdr; + + bzero(&ifdr, sizeof (ifdr)); + strlcpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); + ifdr.ifdr_len = strlen(val); + strlcpy((char *)ifdr.ifdr_desc, val, sizeof (ifdr.ifdr_desc)); + + if (ioctl(s, SIOCSIFDESC, (caddr_t)&ifdr) < 0) { + warn("ioctl (set desc)"); + } +} + +static void +settbr(const char *val, int dummy __unused, int s, const struct afswtch *afp) +{ + struct if_linkparamsreq iflpr; + long double bps; + u_int64_t rate; + u_int32_t percent = 0; + char *cp; + + errno = 0; + bzero(&iflpr, sizeof (iflpr)); + strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); + + bps = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + rate = (u_int64_t)bps; + if (cp != NULL) { + if (!strcmp(cp, "b") || !strcmp(cp, "bps")) { + ; /* nothing */ + } else if (!strcmp(cp, "Kb") || !strcmp(cp, "Kbps")) { + rate *= 1000; + } else if (!strcmp(cp, "Mb") || !strcmp(cp, "Mbps")) { + rate *= 1000 * 1000; + } else if (!strcmp(cp, "Gb") || !strcmp(cp, "Gbps")) { + rate *= 1000 * 1000 * 1000; + } else if (!strcmp(cp, "%")) { + percent = rate; + if (percent == 0 || percent > 100) { + printf("Value out of range '%s'", val); + return; + } + } else if (*cp != '\0') { + printf("Unknown unit '%s'", cp); + return; + } + } + iflpr.iflpr_output_tbr_rate = rate; + iflpr.iflpr_output_tbr_percent = percent; + if (ioctl(s, SIOCSIFLINKPARAMS, &iflpr) < 0 && + errno != ENOENT && errno != ENXIO && errno != ENODEV) { + warn("ioctl (set link params)"); + } else if (errno == ENXIO) { + printf("TBR cannot be set on %s\n", name); + } else if (errno == 0 && rate == 0) { + printf("%s: TBR is now disabled\n", name); + } else if (errno == ENODEV) { + printf("%s: requires absolute TBR rate\n", name); + } else if (percent != 0) { + printf("%s: TBR rate set to %u%% of effective link rate\n", + name, percent); + } else { + printf("%s: TBR rate set to %s\n", name, bps_to_str(rate)); + } +} + +static int +get_int64(uint64_t *i, char const *s) +{ + char *cp; + *i = strtol(s, &cp, 10); + if (cp == s || errno != 0) { + return (-1); + } + return (0); +} + +static int +get_int32(uint32_t *i, char const *s) +{ + char *cp; + *i = strtol(s, &cp, 10); + if (cp == s || errno != 0) { + return (-1); + } + return (0); +} + +static int +get_percent(double *d, const char *s) +{ + char *cp; + *d = strtod(s, &cp) / (double)100; + if (*d == HUGE_VALF || *d == HUGE_VALL) { + return (-1); + } + if (*d == 0.0 || (*cp != '\0' && strcmp(cp, "%") != 0)) { + return (-1); + } + return (0); +} + +static int +get_percent_fixed_point(uint32_t *i, const char *s) +{ + double p; + + if (get_percent(&p, s) != 0){ + return (-1); + } + + *i = p * IF_NETEM_PARAMS_PSCALE; + return (0); +} + +static int +netem_parse_args(struct if_netem_params *p, int argc, char *const *argv) +{ + int argc_saved = argc; + uint64_t bandwitdh = 0; + uint32_t latency = 0, jitter = 0; + uint32_t corruption = 0; + uint32_t duplication = 0; + uint32_t loss_p_gr_gl = 0, loss_p_gr_bl = 0, loss_p_bl_br = 0, + loss_p_bl_gr = 0, loss_p_br_bl = 0; + uint32_t reordering = 0; + + bzero(p, sizeof (*p)); + + /* take out "input"/"output" */ + argc--, argv++; + + for ( ; argc > 0; ) { + if (strcmp(*argv, "bandwidth") == 0) { + argc--, argv++; + if (argc <= 0 || get_int64(&bandwitdh, *argv) != 0) { + err(1, "Invalid value '%s'", *argv); + } + argc--, argv++; + } else if (strcmp(*argv, "corruption") == 0) { + argc--, argv++; + if (argc <= 0 || + get_percent_fixed_point(&corruption, *argv) != 0) { + err(1, "Invalid value '%s'", *argv); + } + argc--, argv++; + } else if (strcmp(*argv, "delay") == 0) { + argc--, argv++; + if (argc <= 0 || get_int32(&latency, *argv) != 0) { + err(1, "Invalid value '%s'", *argv); + } + argc--, argv++; + if (argc > 0 && get_int32(&jitter, *argv) == 0) { + argc--, argv++; + } + } else if (strcmp(*argv, "duplication") == 0) { + argc--, argv++; + if (argc <= 0 || + get_percent_fixed_point(&duplication, *argv) != 0) { + err(1, "Invalid value '%s'", *argv); + return (-1); + } + argc--, argv++; + } else if (strcmp(*argv, "loss") == 0) { + argc--, argv++; + if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_gl, *argv) != 0) { + err(1, "Invalid value '%s'", *argv); + } + /* we may have all 5 probs, use naive model if not */ + argc--, argv++; + if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_bl, *argv) != 0) { + continue; + } + /* if more than p_gr_gl, then should have all probs */ + argc--, argv++; + if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_br, *argv) != 0) { + err(1, "Invalid value '%s' for p_bl_br", *argv); + } + argc--, argv++; + if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_gr, *argv) != 0) { + err(1, "Invalid value '%s' for p_bl_gr", *argv); + } + argc--, argv++; + if (argc <= 0 || get_percent_fixed_point(&loss_p_br_bl, *argv) != 0) { + err(1, "Invalid value '%s' for p_br_bl", *argv); + } + argc--, argv++; + } else if (strcmp(*argv, "reordering") == 0) { + argc--, argv++; + if (argc <= 0 || get_percent_fixed_point(&reordering, *argv) != 0) { + err(1, "Invalid value '%s'", *argv); + } + argc--, argv++; + } else { + return (-1); + } + } + + if (corruption > IF_NETEM_PARAMS_PSCALE) { + err(1, "corruption percentage > 100%%"); + } + + if (duplication > IF_NETEM_PARAMS_PSCALE) { + err(1, "duplication percentage > 100%%"); + } + + if (duplication > 0 && latency == 0) { + /* we need to insert dup'ed packet with latency */ + err(1, "duplication needs latency param"); + } + + if (latency > 1000) { + err(1, "latency %dms too big (> 1 sec)", latency); + } + + if (jitter * 3 > latency) { + err(1, "jitter %dms too big (latency %dms)", jitter, latency); + } + + /* if gr_gl == 0 (no loss), other prob should all be zero */ + if (loss_p_gr_gl == 0 && + (loss_p_gr_bl != 0 || loss_p_bl_br != 0 || loss_p_bl_gr != 0 || + loss_p_br_bl != 0)) { + err(1, "loss params not all zero when gr_gl is zero"); + } + + /* check state machine transition prob integrity */ + if (loss_p_gr_gl > IF_NETEM_PARAMS_PSCALE || + /* gr_gl = IF_NETEM_PARAMS_PSCALE for total loss */ + loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE || + loss_p_bl_br > IF_NETEM_PARAMS_PSCALE || + loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE || + loss_p_br_bl > IF_NETEM_PARAMS_PSCALE || + loss_p_gr_gl + loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE || + loss_p_bl_br + loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE) { + err(1, "loss params too big"); + } + + if (reordering > IF_NETEM_PARAMS_PSCALE) { + err(1, "reordering percentage > 100%%"); + } + + p->ifnetem_bandwidth_bps = bandwitdh; + p->ifnetem_latency_ms = latency; + p->ifnetem_jitter_ms = jitter; + p->ifnetem_corruption_p = corruption; + p->ifnetem_duplication_p = duplication; + p->ifnetem_loss_p_gr_gl = loss_p_gr_gl; + p->ifnetem_loss_p_gr_bl = loss_p_gr_bl; + p->ifnetem_loss_p_bl_br = loss_p_bl_br; + p->ifnetem_loss_p_bl_gr = loss_p_bl_gr; + p->ifnetem_loss_p_br_bl = loss_p_br_bl; + p->ifnetem_reordering_p = reordering; + + return (argc_saved - argc); +} + +void +print_netem_params(struct if_netem_params *p, const char *desc) +{ + struct if_netem_params zero_params; + double pscale = IF_NETEM_PARAMS_PSCALE / 100; + bzero(&zero_params, sizeof (zero_params)); + + if (memcmp(p, &zero_params, sizeof (zero_params)) == 0) { + printf("%s NetEm Disabled\n\n", desc); + } else { + printf( + "%s NetEm Parameters\n" + "\tbandwidth rate %llubps\n" + "\tdelay latency %dms\n" + "\t jitter %dms\n", + desc, p->ifnetem_bandwidth_bps, + p->ifnetem_latency_ms, p->ifnetem_jitter_ms); + if (p->ifnetem_loss_p_gr_bl == 0 && + p->ifnetem_loss_p_bl_br == 0 && + p->ifnetem_loss_p_bl_gr == 0 && + p->ifnetem_loss_p_br_bl == 0) { + printf( + "\tloss %.3f%%\n", + (double) p->ifnetem_loss_p_gr_gl / pscale); + } else { + printf( + "\tloss GAP_RECV -> GAP_LOSS %.3f%%\n" + "\t GAP_RECV -> BURST_LOSS %.3f%%\n" + "\t BURST_LOSS -> BURST_RECV %.3f%%\n" + "\t BURST_LOSS -> GAP_RECV %.3f%%\n" + "\t BURST_RECV -> BURST_LOSS %.3f%%\n", + (double) p->ifnetem_loss_p_gr_gl / pscale, + (double) p->ifnetem_loss_p_gr_bl / pscale, + (double) p->ifnetem_loss_p_bl_br / pscale, + (double) p->ifnetem_loss_p_bl_gr / pscale, + (double) p->ifnetem_loss_p_br_bl / pscale); + } + printf( + "\tcorruption %.3f%%\n" + "\treordering %.3f%%\n\n", + (double) p->ifnetem_corruption_p / pscale, + (double) p->ifnetem_reordering_p / pscale); + } +} + +static int +setnetem(int argc, char *const *argv, int s, const struct afswtch *afp) +{ + struct if_linkparamsreq iflpr; + struct if_netem_params input_params, output_params; + int ret = 0, error = 0; + + bzero(&iflpr, sizeof (iflpr)); + bzero(&input_params, sizeof (input_params)); + bzero(&output_params, sizeof (output_params)); + + if (argc > 1) { + if (strcmp(argv[0], "input") == 0) { + ret = netem_parse_args(&input_params, argc, argv); + } else if (strcmp(argv[0], "output") == 0) { + ret = netem_parse_args(&output_params, argc, argv); + } else if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) { + goto bad_args; + } else { + fprintf(stderr, "uknown option %s\n", argv[0]); + goto bad_args; + } + if (ret < 0) { + goto bad_args; + } + } + + errno = 0; + strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); + error = ioctl(s, SIOCGIFLINKPARAMS, &iflpr); + if (error < 0) { + warn("ioctl (get link params)"); + } + + if (argc == 0) { + print_netem_params(&iflpr.iflpr_input_netem, "Input"); + print_netem_params(&iflpr.iflpr_output_netem, "Output"); + return (0); + } else if (argc == 1) { + if (strcmp(argv[0], "input") == 0) { + bzero(&iflpr.iflpr_input_netem, + sizeof (iflpr.iflpr_input_netem)); + } else if (strcmp(argv[0], "output") == 0) { + bzero(&iflpr.iflpr_output_netem, + sizeof (iflpr.iflpr_output_netem)); + } else { + fprintf(stderr, "uknown option %s\n", argv[0]); + goto bad_args; + } + printf("%s: netem is now disabled for %s\n", name, argv[0]); + ret = 1; + } else { + if (strcmp(argv[0], "input") == 0) { + iflpr.iflpr_input_netem = input_params; + } else if (strcmp(argv[0], "output") == 0) { + iflpr.iflpr_output_netem = output_params; + } + } + + error = ioctl(s, SIOCSIFLINKPARAMS, &iflpr); + if (error < 0 && errno != ENOENT && errno != ENXIO && errno != ENODEV) { + warn("ioctl (set link params)"); + } else if (errno == ENXIO) { + printf("netem cannot be set on %s\n", name); + } else { + printf("%s: netem configured\n", name); + } + + return (ret); +bad_args: + fprintf(stderr, "Usage:\n" + "\tTo enable/set netem params\n" + "\t\tnetem <input|output>\n" + "\t\t [ bandwidth BIT_PER_SEC ]\n" + "\t\t [ delay DELAY_MSEC [ JITTER_MSEC ] ]\n" + "\t\t [ loss PERCENTAGE ]\n" + "\t\t [ duplication PERCENTAGE ]\n" + "\t\t [ reordering PERCENTAGE ]\n\n" + "\tTo disable <input|output> netem\n" + "\t\tnetem <input|output>\n\n" + "\tTo show current settings\n" + "\t\tnetem\n\n"); + return (-1); +} + +static void +setthrottle(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct if_throttlereq iftr; + char *cp; + + errno = 0; + bzero(&iftr, sizeof (iftr)); + strlcpy(iftr.ifthr_name, name, sizeof (iftr.ifthr_name)); + + iftr.ifthr_level = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + + if (ioctl(s, SIOCSIFTHROTTLE, &iftr) < 0 && errno != ENXIO) { + warn("ioctl (set throttling level)"); + } else if (errno == ENXIO) { + printf("throttling level cannot be set on %s\n", name); + } else { + printf("%s: throttling level set to %d\n", name, + iftr.ifthr_level); + } +} + +static void +setdisableoutput(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifreq ifr; + char *cp; + errno = 0; + bzero(&ifr, sizeof (ifr)); + strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + + ifr.ifr_ifru.ifru_disable_output = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + + if (ioctl(s, SIOCSIFDISABLEOUTPUT, &ifr) < 0 && errno != ENXIO) { + warn("ioctl set disable output"); + } else if (errno == ENXIO) { + printf("output thread can not be disabled on %s\n", name); + } else { + printf("output %s on %s\n", + ((ifr.ifr_ifru.ifru_disable_output == 0) ? "enabled" : "disabled"), + name); + } +} + +static void +setlog(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + char *cp; + + errno = 0; + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + ifr.ifr_log.ifl_level = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + ifr.ifr_log.ifl_flags = (IFRLOGF_DLIL|IFRLOGF_FAMILY|IFRLOGF_DRIVER| + IFRLOGF_FIRMWARE); + + if (ioctl(s, SIOCSIFLOG, &ifr) < 0) + warn("ioctl (set logging parameters)"); +} + +void +setcl2k(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_2kcl = value; + + if (ioctl(s, SIOCSIF2KCL, (caddr_t)&ifr) < 0) + Perror(vname); +} + +void +setexpensive(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_expensive = value; + + if (ioctl(s, SIOCSIFEXPENSIVE, (caddr_t)&ifr) < 0) + Perror(vname); +} + +#ifdef SIOCSIFCONSTRAINED +void +setconstrained(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_constrained = value; + + if (ioctl(s, SIOCSIFCONSTRAINED, (caddr_t)&ifr) < 0) + Perror(vname); +} +#endif + +static void +setifmpklog(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_mpk_log = value; + + if (ioctl(s, SIOCSIFMPKLOG, (caddr_t)&ifr) < 0) + Perror(vname); +} + +void +settimestamp(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (value == 0) { + if (ioctl(s, SIOCSIFTIMESTAMPDISABLE, (caddr_t)&ifr) < 0) + Perror(vname); + } else { + if (ioctl(s, SIOCSIFTIMESTAMPENABLE, (caddr_t)&ifr) < 0) + Perror(vname); + } +} + +void +setecnmode(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + char *cp; + + if (strcmp(val, "default") == 0) + ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DEFAULT; + else if (strcmp(val, "enable") == 0) + ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_ENABLE; + else if (strcmp(val, "disable") == 0) + ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DISABLE; + else { + ifr.ifr_ifru.ifru_ecn_mode = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid ECN mode value '%s'", val); + return; + } + } + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (ioctl(s, SIOCSECNMODE, (caddr_t)&ifr) < 0) + Perror("ioctl(SIOCSECNMODE)"); +} + +void +setprobeconnectivity(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_probe_connectivity = value; + + if (ioctl(s, SIOCSIFPROBECONNECTIVITY, (caddr_t)&ifr) < 0) + Perror(vname); +} + +#if defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) + +void +setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + u_long ioc; + +#if (DEBUG | DEVELOPMENT) + printf("%s(%s, %s)\n", __func__, cmd, arg); +#endif /* (DEBUG | DEVELOPMENT) */ + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (strcmp(cmd, "mode") == 0) { + ioc = SIOCSQOSMARKINGMODE; + + if (strcmp(arg, "fastlane") == 0) + ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_FASTLANE; + else if (strcmp(arg, "rfc4594") == 0) + ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_RFC4594; + else if (strcasecmp(arg, "none") == 0 || strcasecmp(arg, "off") == 0) + ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_MODE_NONE; + else + err(EX_USAGE, "bad value for qosmarking mode: %s", arg); + } else if (strcmp(cmd, "enabled") == 0) { + ioc = SIOCSQOSMARKINGENABLED; + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + ifr.ifr_qosmarking_enabled = 1; + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + ifr.ifr_qosmarking_enabled = 0; + else + err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); + } else { + err(EX_USAGE, "qosmarking takes mode or enabled"); + } + + if (ioctl(s, ioc, (caddr_t)&ifr) < 0) + err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); +} + +void +setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + warnx("### fastlane is obsolete, use qosmarking ###"); + + if (strcmp(cmd, "capable") == 0) { + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + setqosmarking("mode", "fastlane", s, afp); + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + setqosmarking("mode", "off", s, afp); + else + err(EX_USAGE, "bad value for fastlane %s", cmd); + } else if (strcmp(cmd, "enable") == 0) { + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + setqosmarking("enabled", "1", s, afp); + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + setqosmarking("enabled", "0", s, afp); + else + err(EX_USAGE, "bad value for fastlane %s", cmd); + } else { + err(EX_USAGE, "fastlane takes capable or enable"); + } +} + +#else /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ + +void +setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + int value; + u_long ioc; + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (strcmp(cmd, "capable") == 0) + ioc = SIOCSFASTLANECAPABLE; + else if (strcmp(cmd, "enable") == 0) + ioc = SIOCSFASTLEENABLED; + else + err(EX_USAGE, "fastlane takes capable or enabled"); + + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + value = 1; + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + value = 0; + else + err(EX_USAGE, "bad value for fastlane %s", cmd); + + if (ioc == SIOCSFASTLANECAPABLE) + ifr.ifr_fastlane_capable = value; + else + ifr.ifr_fastlane_enabled = value; + + if (ioctl(s, ioc, (caddr_t)&ifr) < 0) + err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); +} + + +void +setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + if (strcmp(cmd, "mode") == 0) { + if (strcmp(arg, "fastlane") == 0) + setfastlane("capable", "on", s, afp); + else if (strcmp(arg, "none") == 0) + setfastlane("capable", "off", s, afp); + else + err(EX_USAGE, "bad value for qosmarking mode: %s", arg); + } else if (strcmp(cmd, "enabled") == 0) { + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + setfastlane("enable", "on", s, afp); + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + setfastlane("enable", "off", s, afp); + else + err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); + } else { + err(EX_USAGE, "qosmarking takes mode or enabled"); + } +} + +#endif /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ + +void +setlowpowermode(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_low_power_mode = !!value; + + if (ioctl(s, SIOCSIFLOWPOWER, (caddr_t)&ifr) < 0) + Perror(vname); +} + +struct str2num { + const char *str; + uint32_t num; +}; + +static struct str2num subfamily_str2num[] = { + { .str = "any", .num = IFRTYPE_SUBFAMILY_ANY }, + { .str = "USB", .num = IFRTYPE_SUBFAMILY_USB }, + { .str = "Bluetooth", .num = IFRTYPE_SUBFAMILY_BLUETOOTH }, + { .str = "Wi-Fi", .num = IFRTYPE_SUBFAMILY_WIFI }, + { .str = "wifi", .num = IFRTYPE_SUBFAMILY_WIFI }, + { .str = "Thunderbolt", .num = IFRTYPE_SUBFAMILY_THUNDERBOLT }, + { .str = "reserverd", .num = IFRTYPE_SUBFAMILY_RESERVED }, + { .str = "intcoproc", .num = IFRTYPE_SUBFAMILY_INTCOPROC }, + { .str = "QuickRelay", .num = IFRTYPE_SUBFAMILY_QUICKRELAY }, + { .str = "Default", .num = IFRTYPE_SUBFAMILY_DEFAULT }, + { .str = NULL, .num = 0 }, +}; + +static uint32_t +get_num_from_str(struct str2num* str2nums, const char *str) +{ + struct str2num *str2num = str2nums; + + while (str2num != NULL && str2num->str != NULL) { + if (strcasecmp(str2num->str, str) == 0) { + return str2num->num; + } + str2num++; + } + return 0; +} + +static void +setifsubfamily(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + + char *endptr; + uint32_t subfamily = strtoul(val, &endptr, 0); + if (*endptr != 0) { + subfamily = get_num_from_str(subfamily_str2num, val); + if (subfamily == 0) { + return; + } + } + + ifr.ifr_type.ift_subfamily = subfamily; + if (ioctl(s, SIOCSIFSUBFAMILY, (caddr_t)&ifr) < 0) + warn("ioctl(SIOCSIFSUBFAMILY)"); +} + +void +setifavailability(const char *vname, int value, int s, const struct afswtch *afp) +{ + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_interface_state.valid_bitmask = IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID; + if (value == 0) { + ifr.ifr_interface_state.interface_availability = IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE; + } else { + ifr.ifr_interface_state.interface_availability = IF_INTERFACE_STATE_INTERFACE_AVAILABLE; + } + if (ioctl(s, SIOCSIFINTERFACESTATE, (caddr_t)&ifr) < 0) + warn("ioctl(SIOCSIFINTERFACESTATE)"); +} + +static void +show_routermode(int s) +{ + struct afswtch *afp; + + afp = af_getbyname("inet"); + if (afp != NULL) { + (*afp->af_routermode)(s, 0, NULL); + } +} + +static void +show_routermode6(void) +{ + struct afswtch *afp; + static int s = -1; + + afp = af_getbyname("inet6"); + if (afp != NULL) { + if (s < 0) { + s = socket(AF_INET6, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return; + } + } + (*afp->af_routermode)(s, 0, NULL); + } +} + +#define IFFBITS \ +"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ +"\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ +"\20MULTICAST" + +#define IFEFBITS \ +"\020\1AUTOCONFIGURING\4PROBE_CONNECTIVITY\5FASTLN_CAP\6IPV6_DISABLED\7ACCEPT_RTADV\10TXSTART\11RXPOLL" \ +"\12VLAN\13BOND\14ARPLL\15CLAT46\16NOAUTOIPV6LL\17EXPENSIVE\20ROUTER4" \ +"\22LOCALNET_PRIVATE\23ND6ALT\24RESTRICTED_RECV\25AWDL\26NOACKPRI" \ +"\27AWDL_RESTRICTED\30CL2K\31ECN_ENABLE\32ECN_DISABLE\33CHANNEL_DRV\34CA" \ +"\35SENDLIST\36DIRECTLINK\37FASTLN_ON\40UPDOWNCHANGE" + +#define IFXFBITS \ +"\020\1WOL\2TIMESTAMP\3NOAUTONX\4LEGACY\5TXLOWINET\6RXLOWINET\7ALLOCKPI" \ +"\10LOWPOWER\11MPKLOG\12CONSTRAINED" + +#define IFCAPBITS \ +"\020\1RXCSUM\2TXCSUM\3VLAN_MTU\4VLAN_HWTAGGING\5JUMBO_MTU" \ +"\6TSO4\7TSO6\10LRO\11AV\12TXSTATUS\13CHANNEL_IO\14HW_TIMESTAMP\15SW_TIMESTAMP" \ +"\16PARTIAL_CSUM\17ZEROINVERT_CSUM" + +#define IFRLOGF_BITS \ +"\020\1DLIL\21FAMILY\31DRIVER\35FIRMWARE" + +/* + * Print the status of the interface. If an address family was + * specified, show only it; otherwise, show them all. + */ +static void +status(const struct afswtch *afp, const struct sockaddr_dl *sdl, + struct ifaddrs *ifa) +{ + struct ifaddrs *ift; + int allfamilies, s; + struct ifstat ifs; + struct if_descreq ifdr; + struct if_linkparamsreq iflpr; + int mib[6]; + struct ifmibdata_supplemental ifmsupp; + size_t miblen = sizeof(struct ifmibdata_supplemental); + u_int64_t eflags = 0; + u_int64_t xflags = 0; + int curcap = 0; + + if (afp == NULL) { + allfamilies = 1; + afp = af_getbyname("inet"); + } else + allfamilies = 0; + + ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af; + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); + if (s < 0) + err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); + + printf("%s: ", name); + printb("flags", ifa->ifa_flags, IFFBITS); + if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) + if (ifr.ifr_metric) + printf(" metric %d", ifr.ifr_metric); + if (ioctl(s, SIOCGIFMTU, &ifr) != -1) + printf(" mtu %d", ifr.ifr_mtu); + if (showrtref && ioctl(s, SIOCGIFGETRTREFCNT, &ifr) != -1) + printf(" rtref %d", ifr.ifr_route_refcnt); + if (verbose) { + unsigned int ifindex = if_nametoindex(ifa->ifa_name); + if (ifindex != 0) + printf(" index %u", ifindex); + } +#ifdef SIOCGIFCONSTRAINED + // Constrained is stored in if_xflags which isn't exposed directly + if (ioctl(s, SIOCGIFCONSTRAINED, (caddr_t)&ifr) == 0 && + ifr.ifr_constrained != 0) { + printf(" constrained"); + } +#endif + putchar('\n'); + + if (verbose && ioctl(s, SIOCGIFEFLAGS, (caddr_t)&ifr) != -1 && + (eflags = ifr.ifr_eflags) != 0) { + printb("\teflags", eflags, IFEFBITS); + putchar('\n'); + } + + if (verbose && ioctl(s, SIOCGIFXFLAGS, (caddr_t)&ifr) != -1 && + (xflags = ifr.ifr_xflags) != 0) { + printb("\txflags", xflags, IFXFBITS); + putchar('\n'); + } + + if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { + if (ifr.ifr_curcap != 0) { + curcap = ifr.ifr_curcap; + printb("\toptions", ifr.ifr_curcap, IFCAPBITS); + putchar('\n'); + } + if (supmedia && ifr.ifr_reqcap != 0) { + printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); + putchar('\n'); + } + } + + tunnel_status(s); + + for (ift = ifa; ift != NULL; ift = ift->ifa_next) { + if (ift->ifa_addr == NULL) + continue; + if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) + continue; + if (allfamilies) { + const struct afswtch *p; + p = af_getbyfamily(ift->ifa_addr->sa_family); + if (p != NULL && p->af_status != NULL) + p->af_status(s, ift); + } else if (afp->af_af == ift->ifa_addr->sa_family) + afp->af_status(s, ift); + } + +/* Print CLAT46 address */ + clat46_addr(s, name); + +/* Print NAT64 prefix */ + nat64_status(s, name); + +#if 0 + if (allfamilies || afp->af_af == AF_LINK) { + const struct afswtch *lafp; + + /* + * Hack; the link level address is received separately + * from the routing information so any address is not + * handled above. Cobble together an entry and invoke + * the status method specially. + */ + lafp = af_getbyname("lladdr"); + if (lafp != NULL) { + info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; + lafp->af_status(s, &info); + } + } +#endif + if (allfamilies) + af_other_status(s); + else if (afp->af_other_status != NULL) + afp->af_other_status(s); + + strlcpy(ifs.ifs_name, name, sizeof ifs.ifs_name); + if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) + printf("%s", ifs.ascii); + + /* The rest is for when verbose is set; if not set, we're done */ + if (!verbose) + goto done; + + if (ioctl(s, SIOCGIFTYPE, &ifr) != -1) { + char *c = ift2str(ifr.ifr_type.ift_type, + ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily); + if (c != NULL) + printf("\ttype: %s\n", c); + } + + if (verbose > 1) { + if (ioctl(s, SIOCGIFFUNCTIONALTYPE, &ifr) != -1) { + char *c = iffunct2str(ifr.ifr_functional_type); + if (c != NULL) + printf("\tfunctional type: %s\n", c); + } + } + { + struct if_agentidsreq ifar; + memset(&ifar, 0, sizeof(ifar)); + + strlcpy(ifar.ifar_name, name, sizeof(ifar.ifar_name)); + + if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != -1) { + if (ifar.ifar_count != 0) { + ifar.ifar_uuids = calloc(ifar.ifar_count, sizeof(uuid_t)); + if (ifar.ifar_uuids != NULL) { + if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != 1) { + for (int agent_i = 0; agent_i < ifar.ifar_count; agent_i++) { + struct netagent_req nar; + memset(&nar, 0, sizeof(nar)); + + uuid_copy(nar.netagent_uuid, ifar.ifar_uuids[agent_i]); + + if (ioctl(s, SIOCGIFAGENTDATA, &nar) != 1) { + printf("\tagent domain:%s type:%s flags:0x%x desc:\"%s\"\n", + nar.netagent_domain, nar.netagent_type, + nar.netagent_flags, nar.netagent_desc); + } + } + } + free(ifar.ifar_uuids); + } + } + } + } + + if (ioctl(s, SIOCGIFLINKQUALITYMETRIC, &ifr) != -1) { + int lqm = ifr.ifr_link_quality_metric; + if (verbose > 1) { + printf("\tlink quality: %d ", lqm); + if (lqm == IFNET_LQM_THRESH_OFF) + printf("(off)"); + else if (lqm == IFNET_LQM_THRESH_UNKNOWN) + printf("(unknown)"); + else if (lqm > IFNET_LQM_THRESH_UNKNOWN && + lqm <= IFNET_LQM_THRESH_BAD) + printf("(bad)"); + else if (lqm > IFNET_LQM_THRESH_UNKNOWN && + lqm <= IFNET_LQM_THRESH_POOR) + printf("(poor)"); + else if (lqm > IFNET_LQM_THRESH_POOR && + lqm <= IFNET_LQM_THRESH_GOOD) + printf("(good)"); + else + printf("(?)"); + printf("\n"); + } else if (lqm > IFNET_LQM_THRESH_UNKNOWN) { + printf("\tlink quality: %d ", lqm); + if (lqm <= IFNET_LQM_THRESH_BAD) + printf("(bad)"); + else if (lqm <= IFNET_LQM_THRESH_POOR) + printf("(poor)"); + else if (lqm <= IFNET_LQM_THRESH_GOOD) + printf("(good)"); + else + printf("(?)"); + printf("\n"); + } + } + + { + if (ioctl(s, SIOCGIFINTERFACESTATE, &ifr) != -1) { + printf("\tstate"); + if (ifr.ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_RRC_STATE_VALID) { + uint8_t rrc_state = ifr.ifr_interface_state.rrc_state; + + printf(" rrc: %u ", rrc_state); + if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_CONNECTED) + printf("(connected)"); + else if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_IDLE) + printf("(idle)"); + else + printf("(?)"); + } + if (ifr.ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) { + uint8_t ifavail = ifr.ifr_interface_state.interface_availability; + + printf(" availability: %u ", ifavail); + if (ifavail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE) + printf("(true)"); + else if (ifavail == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE) + printf("(false)"); + else + printf("(?)"); + } else { + printf(" availability: (not valid)"); + } + if (verbose > 1 && + ifr.ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_LQM_STATE_VALID) { + int8_t lqm = ifr.ifr_interface_state.lqm_state; + + printf(" lqm: %d", lqm); + + if (lqm == IFNET_LQM_THRESH_OFF) + printf("(off)"); + else if (lqm == IFNET_LQM_THRESH_UNKNOWN) + printf("(unknown)"); + else if (lqm == IFNET_LQM_THRESH_BAD) + printf("(bad)"); + else if (lqm == IFNET_LQM_THRESH_POOR) + printf("(poor)"); + else if (lqm == IFNET_LQM_THRESH_GOOD) + printf("(good)"); + else + printf("(?)"); + } + } + printf("\n"); + } + + bzero(&iflpr, sizeof (iflpr)); + strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); + if (ioctl(s, SIOCGIFLINKPARAMS, &iflpr) != -1) { + u_int64_t ibw_max = iflpr.iflpr_input_bw.max_bw; + u_int64_t ibw_eff = iflpr.iflpr_input_bw.eff_bw; + u_int64_t obw_max = iflpr.iflpr_output_bw.max_bw; + u_int64_t obw_eff = iflpr.iflpr_output_bw.eff_bw; + u_int64_t obw_tbr = iflpr.iflpr_output_tbr_rate; + u_int32_t obw_pct = iflpr.iflpr_output_tbr_percent; + u_int64_t ilt_max = iflpr.iflpr_input_lt.max_lt; + u_int64_t ilt_eff = iflpr.iflpr_input_lt.eff_lt; + u_int64_t olt_max = iflpr.iflpr_output_lt.max_lt; + u_int64_t olt_eff = iflpr.iflpr_output_lt.eff_lt; + + + if (eflags & IFEF_TXSTART) { + u_int32_t flags = iflpr.iflpr_flags; + u_int32_t sched = iflpr.iflpr_output_sched; + struct if_throttlereq iftr; + + printf("\tscheduler: %s%s ", + (flags & IFLPRF_ALTQ) ? "ALTQ_" : "", + sched2str(sched)); + if (flags & IFLPRF_DRVMANAGED) + printf("(driver managed)"); + printf("\n"); + + bzero(&iftr, sizeof (iftr)); + strlcpy(iftr.ifthr_name, name, + sizeof (iftr.ifthr_name)); + if (ioctl(s, SIOCGIFTHROTTLE, &iftr) != -1 && + iftr.ifthr_level != IFNET_THROTTLE_OFF) + printf("\tthrottling: level %d (%s)\n", + iftr.ifthr_level, tl2str(iftr.ifthr_level)); + } + + if (obw_tbr != 0 && obw_eff > obw_tbr) + obw_eff = obw_tbr; + + if (ibw_max != 0 || obw_max != 0) { + if (ibw_max == obw_max && ibw_eff == obw_eff && + ibw_max == ibw_eff && obw_tbr == 0) { + printf("\tlink rate: %s\n", + bps_to_str(ibw_max)); + } else { + printf("\tuplink rate: %s [eff] / ", + bps_to_str(obw_eff)); + if (obw_tbr != 0) { + if (obw_pct == 0) + printf("%s [tbr] / ", + bps_to_str(obw_tbr)); + else + printf("%s [tbr %u%%] / ", + bps_to_str(obw_tbr), + obw_pct); + } + printf("%s", bps_to_str(obw_max)); + if (obw_tbr != 0) + printf(" [max]"); + printf("\n"); + if (ibw_eff == ibw_max) { + printf("\tdownlink rate: %s\n", + bps_to_str(ibw_max)); + } else { + printf("\tdownlink rate: " + "%s [eff] / ", bps_to_str(ibw_eff)); + printf("%s [max]\n", + bps_to_str(ibw_max)); + } + } + } else if (obw_tbr != 0) { + printf("\tuplink rate: %s [tbr]\n", + bps_to_str(obw_tbr)); + } + + if (ilt_max != 0 || olt_max != 0) { + if (ilt_max == olt_max && ilt_eff == olt_eff && + ilt_max == ilt_eff) { + printf("\tlink latency: %s\n", + ns_to_str(ilt_max)); + } else { + if (olt_max != 0 && olt_eff == olt_max) { + printf("\tuplink latency: %s\n", + ns_to_str(olt_max)); + } else if (olt_max != 0) { + printf("\tuplink latency: " + "%s [eff] / ", ns_to_str(olt_eff)); + printf("%s [max]\n", + ns_to_str(olt_max)); + } + if (ilt_max != 0 && ilt_eff == ilt_max) { + printf("\tdownlink latency: %s\n", + ns_to_str(ilt_max)); + } else if (ilt_max != 0) { + printf("\tdownlink latency: " + "%s [eff] / ", ns_to_str(ilt_eff)); + printf("%s [max]\n", + ns_to_str(ilt_max)); + } + } + } + } + + /* Common OID prefix */ + mib[0] = CTL_NET; + mib[1] = PF_LINK; + mib[2] = NETLINK_GENERIC; + mib[3] = IFMIB_IFDATA; + mib[4] = if_nametoindex(name); + mib[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(mib, 6, &ifmsupp, &miblen, (void *)0, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL"); + + if (ifmsupp.ifmd_data_extended.ifi_alignerrs != 0) { + printf("\tunaligned pkts: %llu\n", + ifmsupp.ifmd_data_extended.ifi_alignerrs); + } + if (ifmsupp.ifmd_data_extended.ifi_dt_bytes != 0) { + printf("\tdata milestone interval: %s\n", + bytes_to_str(ifmsupp.ifmd_data_extended.ifi_dt_bytes)); + } + + bzero(&ifdr, sizeof (ifdr)); + strlcpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); + if (ioctl(s, SIOCGIFDESC, &ifdr) != -1 && ifdr.ifdr_len) { + printf("\tdesc: %s\n", ifdr.ifdr_desc); + } + + if (ioctl(s, SIOCGIFLOG, &ifr) != -1 && ifr.ifr_log.ifl_level) { + printf("\tlogging: level %d ", ifr.ifr_log.ifl_level); + printb("facilities", ifr.ifr_log.ifl_flags, IFRLOGF_BITS); + putchar('\n'); + } + + if (ioctl(s, SIOCGIFDELEGATE, &ifr) != -1 && ifr.ifr_delegated) { + char delegatedif[IFNAMSIZ+1]; + if (if_indextoname(ifr.ifr_delegated, delegatedif) != NULL) + printf("\teffective interface: %s\n", delegatedif); + } + + if (ioctl(s, SIOCGSTARTDELAY, &ifr) != -1) { + if (ifr.ifr_start_delay_qlen > 0 && + ifr.ifr_start_delay_timeout > 0) { + printf("\ttxstart qlen: %u packets " + "timeout: %u microseconds\n", + ifr.ifr_start_delay_qlen, + ifr.ifr_start_delay_timeout/1000); + } + } +#if defined(IFCAP_HW_TIMESTAMP) && defined(IFCAP_SW_TIMESTAMP) + if ((curcap & (IFCAP_HW_TIMESTAMP | IFCAP_SW_TIMESTAMP)) && + ioctl(s, SIOCGIFTIMESTAMPENABLED, &ifr) != -1) { + printf("\ttimestamp: %s\n", + (ifr.ifr_intval != 0) ? "enabled" : "disabled"); + } +#endif +#if defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) + if (ioctl(s, SIOCGQOSMARKINGENABLED, &ifr) != -1) { + printf("\tqosmarking enabled: %s mode: ", + ifr.ifr_qosmarking_enabled ? "yes" : "no"); + if (ioctl(s, SIOCGQOSMARKINGMODE, &ifr) != -1) { + switch (ifr.ifr_qosmarking_mode) { + case IFRTYPE_QOSMARKING_FASTLANE: + printf("fastlane\n"); + break; + case IFRTYPE_QOSMARKING_RFC4594: + printf("RFC4594\n"); + break; + case IFRTYPE_QOSMARKING_MODE_NONE: + printf("none\n"); + break; + default: + printf("unknown (%u)\n", ifr.ifr_qosmarking_mode); + break; + } + } + } +#endif /* defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) */ + + if (ioctl(s, SIOCGIFLOWPOWER, &ifr) != -1) { + printf("\tlow power mode: %s\n", + (ifr.ifr_low_power_mode != 0) ? "enabled" : "disabled"); + } + if (ioctl(s, SIOCGIFMPKLOG, &ifr) != -1) { + printf("\tmulti layer packet logging (mpklog): %s\n", + (ifr.ifr_mpk_log != 0) ? "enabled" : "disabled"); + } + show_routermode(s); + show_routermode6(); + +done: + close(s); + return; +} + +#define KILOBYTES 1024 +#define MEGABYTES (KILOBYTES * KILOBYTES) +#define GIGABYTES (KILOBYTES * KILOBYTES * KILOBYTES) + +static char * +bytes_to_str(unsigned long long bytes) +{ + static char buf[32]; + const char *u; + long double n = bytes, t; + + if (bytes >= GIGABYTES) { + t = n / GIGABYTES; + u = "GB"; + } else if (n >= MEGABYTES) { + t = n / MEGABYTES; + u = "MB"; + } else if (n >= KILOBYTES) { + t = n / KILOBYTES; + u = "KB"; + } else { + t = n; + u = "bytes"; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %s", t, u); + return (buf); +} + +#define GIGABIT_PER_SEC 1000000000 /* gigabit per second */ +#define MEGABIT_PER_SEC 1000000 /* megabit per second */ +#define KILOBIT_PER_SEC 1000 /* kilobit per second */ + +static char * +bps_to_str(unsigned long long rate) +{ + static char buf[32]; + const char *u; + long double n = rate, t; + + if (rate >= GIGABIT_PER_SEC) { + t = n / GIGABIT_PER_SEC; + u = "Gbps"; + } else if (n >= MEGABIT_PER_SEC) { + t = n / MEGABIT_PER_SEC; + u = "Mbps"; + } else if (n >= KILOBIT_PER_SEC) { + t = n / KILOBIT_PER_SEC; + u = "Kbps"; + } else { + t = n; + u = "bps "; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); + return (buf); +} + +#define NSEC_PER_SEC 1000000000 /* nanosecond per second */ +#define USEC_PER_SEC 1000000 /* microsecond per second */ +#define MSEC_PER_SEC 1000 /* millisecond per second */ + +static char * +ns_to_str(unsigned long long nsec) +{ + static char buf[32]; + const char *u; + long double n = nsec, t; + + if (nsec >= NSEC_PER_SEC) { + t = n / NSEC_PER_SEC; + u = "sec "; + } else if (n >= USEC_PER_SEC) { + t = n / USEC_PER_SEC; + u = "msec"; + } else if (n >= MSEC_PER_SEC) { + t = n / MSEC_PER_SEC; + u = "usec"; + } else { + t = n; + u = "nsec"; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); + return (buf); +} + +static void +tunnel_status(int s) +{ + af_all_tunnel_status(s); +} + +static void +clat46_addr(int s, char * if_name) +{ + struct if_clat46req ifr; + char buf[MAXHOSTNAMELEN]; + + bzero(&ifr, sizeof (ifr)); + strlcpy(ifr.ifclat46_name, if_name, sizeof(ifr.ifclat46_name)); + + if (ioctl(s, SIOCGIFCLAT46ADDR, &ifr) < 0) { + if (errno != ENOENT) + syslog(LOG_WARNING, "ioctl (SIOCGIFCLAT46ADDR): %d", errno); + return; + } + + if (inet_ntop(AF_INET6, &ifr.ifclat46_addr.v6_address, buf, sizeof(buf)) != NULL) + printf("\tinet6 %s prefixlen %d clat46\n", + buf, ifr.ifclat46_addr.v6_prefixlen); +} + +static void +nat64_status(int s, char * if_name) +{ + int i; + struct if_nat64req ifr; + char buf[MAXHOSTNAMELEN]; + + bzero(&ifr, sizeof(ifr)); + strlcpy(ifr.ifnat64_name, if_name, sizeof(ifr.ifnat64_name)); + + if (ioctl(s, SIOCGIFNAT64PREFIX, &ifr) < 0) { + if (errno != ENOENT) + syslog(LOG_WARNING, "ioctl(SIOCGIFNAT64PREFIX): %d", errno); + return; + } + + for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) { + if (ifr.ifnat64_prefixes[i].prefix_len > 0) { + inet_ntop(AF_INET6, &ifr.ifnat64_prefixes[i].ipv6_prefix, buf, sizeof(buf)); + printf("\tnat64 prefix %s prefixlen %d\n", + buf, ifr.ifnat64_prefixes[i].prefix_len << 3); + } + } +} + +void +Perror(const char *cmd) +{ + switch (errno) { + + case ENXIO: + errx(1, "%s: no such interface", cmd); + break; + + case EPERM: + errx(1, "%s: permission denied", cmd); + break; + + default: + err(1, "%s", cmd); + } +} + +/* + * Print a value a la the %b format of the kernel's printf + */ +void +printb(const char *s, unsigned v, const char *bits) +{ + int i, any = 0; + char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++) != '\0') { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +#ifndef __APPLE__ +void +ifmaybeload(const char *name) +{ +#define MOD_PREFIX_LEN 3 /* "if_" */ + struct module_stat mstat; + int fileid, modid; + char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; + const char *cp; + + /* loading suppressed by the user */ + if (noload) + return; + + /* trim the interface number off the end */ + strlcpy(ifname, name, sizeof(ifname)); + for (dp = ifname; *dp != 0; dp++) + if (isdigit(*dp)) { + *dp = 0; + break; + } + + /* turn interface and unit into module name */ + strlcpy(ifkind, "if_", sizeof(ifkind)); + strlcpy(ifkind + MOD_PREFIX_LEN, ifname, + sizeof(ifkind) - MOD_PREFIX_LEN); + + /* scan files in kernel */ + mstat.version = sizeof(struct module_stat); + for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { + /* scan modules in file */ + for (modid = kldfirstmod(fileid); modid > 0; + modid = modfnext(modid)) { + if (modstat(modid, &mstat) < 0) + continue; + /* strip bus name if present */ + if ((cp = strchr(mstat.name, '/')) != NULL) { + cp++; + } else { + cp = mstat.name; + } + /* already loaded? */ + if (strncmp(ifname, cp, strlen(ifname) + 1) == 0 || + strncmp(ifkind, cp, strlen(ifkind) + 1) == 0) + return; + } + } + + /* not present, we should try to load it */ + kldload(ifkind); +} +#endif + +static struct cmd basic_cmds[] = { + DEF_CMD("up", IFF_UP, setifflags), + DEF_CMD("down", -IFF_UP, setifflags), + DEF_CMD("arp", -IFF_NOARP, setifflags), + DEF_CMD("-arp", IFF_NOARP, setifflags), + DEF_CMD("debug", IFF_DEBUG, setifflags), + DEF_CMD("-debug", -IFF_DEBUG, setifflags), +#ifdef IFF_PPROMISC + DEF_CMD("promisc", IFF_PPROMISC, setifflags), + DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), +#endif /* IFF_PPROMISC */ + DEF_CMD("add", IFF_UP, notealias), + DEF_CMD("alias", IFF_UP, notealias), + DEF_CMD("-alias", -IFF_UP, notealias), + DEF_CMD("delete", -IFF_UP, notealias), + DEF_CMD("remove", -IFF_UP, notealias), +#ifdef notdef +#define EN_SWABIPS 0x1000 + DEF_CMD("swabips", EN_SWABIPS, setifflags), + DEF_CMD("-swabips", -EN_SWABIPS, setifflags), +#endif + DEF_CMD_ARG("netmask", setifnetmask), + DEF_CMD_ARG("metric", setifmetric), + DEF_CMD_ARG("broadcast", setifbroadaddr), + DEF_CMD_ARG("ipdst", setifipdst), + DEF_CMD_ARG2("tunnel", settunnel), + DEF_CMD("-tunnel", 0, deletetunnel), + DEF_CMD("deletetunnel", 0, deletetunnel), + DEF_CMD("link0", IFF_LINK0, setifflags), + DEF_CMD("-link0", -IFF_LINK0, setifflags), + DEF_CMD("link1", IFF_LINK1, setifflags), + DEF_CMD("-link1", -IFF_LINK1, setifflags), + DEF_CMD("link2", IFF_LINK2, setifflags), + DEF_CMD("-link2", -IFF_LINK2, setifflags), +#ifdef IFF_MONITOR + DEF_CMD("monitor", IFF_MONITOR:, setifflags), + DEF_CMD("-monitor", -IFF_MONITOR, setifflags), +#endif /* IFF_MONITOR */ + DEF_CMD("mpklog", 1, setifmpklog), + DEF_CMD("-mpklog", 0, setifmpklog), +#ifdef IFF_STATICARP + DEF_CMD("staticarp", IFF_STATICARP, setifflags), + DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), +#endif /* IFF_STATICARP */ +#ifdef IFCAP_RXCSUM + DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), + DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), +#endif /* IFCAP_RXCSUM */ +#ifdef IFCAP_TXCSUM + DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), + DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), +#endif /* IFCAP_TXCSUM */ +#ifdef IFCAP_NETCONS + DEF_CMD("netcons", IFCAP_NETCONS, setifcap), + DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), +#endif /* IFCAP_NETCONS */ +#ifdef IFCAP_POLLING + DEF_CMD("polling", IFCAP_POLLING, setifcap), + DEF_CMD("-polling", -IFCAP_POLLING, setifcap), +#endif /* IFCAP_POLLING */ +#ifdef IFCAP_TSO + DEF_CMD("tso", IFCAP_TSO, setifcap), + DEF_CMD("-tso", -IFCAP_TSO, setifcap), +#endif /* IFCAP_TSO */ +#ifdef IFCAP_LRO + DEF_CMD("lro", IFCAP_LRO, setifcap), + DEF_CMD("-lro", -IFCAP_LRO, setifcap), +#endif /* IFCAP_LRO */ +#ifdef IFCAP_WOL + DEF_CMD("wol", IFCAP_WOL, setifcap), + DEF_CMD("-wol", -IFCAP_WOL, setifcap), +#endif /* IFCAP_WOL */ +#ifdef IFCAP_WOL_UCAST + DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), + DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), +#endif /* IFCAP_WOL_UCAST */ +#ifdef IFCAP_WOL_MCAST + DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), + DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), +#endif /* IFCAP_WOL_MCAST */ +#ifdef IFCAP_WOL_MAGIC + DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), + DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), +#endif /* IFCAP_WOL_MAGIC */ + DEF_CMD("normal", -IFF_LINK0, setifflags), + DEF_CMD("compress", IFF_LINK0, setifflags), + DEF_CMD("noicmp", IFF_LINK1, setifflags), + DEF_CMD_ARG("mtu", setifmtu), +#ifdef notdef + DEF_CMD_ARG("name", setifname), +#endif /* notdef */ +#ifdef IFCAP_AV + DEF_CMD("av", IFCAP_AV, setifcap), + DEF_CMD("-av", -IFCAP_AV, setifcap), +#endif /* IFCAP_AV */ + DEF_CMD("router", 1, setrouter), + DEF_CMD("-router", 0, setrouter), + DEF_CMD_VA("routermode", routermode), + DEF_CMD_ARG("desc", setifdesc), + DEF_CMD_ARG("tbr", settbr), + DEF_CMD_VA("netem", setnetem), + DEF_CMD_ARG("throttle", setthrottle), + DEF_CMD_ARG("log", setlog), + DEF_CMD("cl2k", 1, setcl2k), + DEF_CMD("-cl2k", 0, setcl2k), + DEF_CMD("expensive", 1, setexpensive), + DEF_CMD("-expensive", 0, setexpensive), +#ifdef SIOCSIFCONSTRAINED + DEF_CMD("constrained", 1, setconstrained), + DEF_CMD("-constrained", 0, setconstrained), +#endif + DEF_CMD("timestamp", 1, settimestamp), + DEF_CMD("-timestamp", 0, settimestamp), + DEF_CMD_ARG("ecn", setecnmode), + DEF_CMD_ARG2("fastlane", setfastlane), + DEF_CMD_ARG2("qosmarking", setqosmarking), + DEF_CMD_ARG("disable_output", setdisableoutput), + DEF_CMD("probe_connectivity", 1, setprobeconnectivity), + DEF_CMD("-probe_connectivity", 0, setprobeconnectivity), + DEF_CMD("lowpowermode", 1, setlowpowermode), + DEF_CMD("-lowpowermode", 0, setlowpowermode), + DEF_CMD_ARG("subfamily", setifsubfamily), + DEF_CMD("available", 1, setifavailability), + DEF_CMD("-available", 0, setifavailability), + DEF_CMD("unavailable", 0, setifavailability), +}; + +static __constructor void +ifconfig_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(basic_cmds); i++) + cmd_register(&basic_cmds[i]); +#undef N +} + +static char * +sched2str(unsigned int s) +{ + char *c; + + switch (s) { + case PKTSCHEDT_NONE: + c = "NONE"; + break; + case PKTSCHEDT_FQ_CODEL: + c = "FQ_CODEL"; + break; + default: + c = "UNKNOWN"; + break; + } + + return (c); +} + +static char * +tl2str(unsigned int s) +{ + char *c; + + switch (s) { + case IFNET_THROTTLE_OFF: + c = "off"; + break; + case IFNET_THROTTLE_OPPORTUNISTIC: + c = "opportunistic"; + break; + default: + c = "unknown"; + break; + } + + return (c); +} + +static char * +ift2str(unsigned int t, unsigned int f, unsigned int sf) +{ + static char buf[256]; + char *c = NULL; + + switch (t) { + case IFT_ETHER: + switch (sf) { + case IFRTYPE_SUBFAMILY_USB: + c = "USB Ethernet"; + break; + case IFRTYPE_SUBFAMILY_BLUETOOTH: + c = "Bluetooth PAN"; + break; + case IFRTYPE_SUBFAMILY_WIFI: + c = "Wi-Fi"; + break; + case IFRTYPE_SUBFAMILY_THUNDERBOLT: + c = "IP over Thunderbolt"; + break; + case IFRTYPE_SUBFAMILY_ANY: + default: + c = "Ethernet"; + break; + } + break; + + case IFT_IEEE1394: + c = "IP over FireWire"; + break; + + case IFT_PKTAP: + c = "Packet capture"; + break; + + case IFT_CELLULAR: + c = "Cellular"; + break; + + case IFT_OTHER: + if (ifr.ifr_type.ift_family == APPLE_IF_FAM_IPSEC) { + if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_BLUETOOTH) { + c = "Companion Link Bluetooth"; + } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_QUICKRELAY) { + c = "Companion Link QuickRelay"; + } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_WIFI) { + c = "Companion Link Wi-Fi"; + } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_DEFAULT) { + c = "Companion Link Default"; + } + } + break; + + case IFT_BRIDGE: + case IFT_PFLOG: + case IFT_PFSYNC: + case IFT_PPP: + case IFT_LOOP: + case IFT_GIF: + case IFT_STF: + case IFT_L2VLAN: + case IFT_IEEE8023ADLAG: + default: + break; + } + + if (verbose > 1) { + if (c == NULL) { + (void) snprintf(buf, sizeof (buf), + "0x%x family: %u subfamily: %u", + ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, + ifr.ifr_type.ift_subfamily); + } else { + (void) snprintf(buf, sizeof (buf), + "%s (0x%x) family: %u subfamily: %u", c, + ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, + ifr.ifr_type.ift_subfamily); + } + c = buf; + } + + return (c); +} + +static char * +iffunct2str(u_int32_t functional_type) +{ + char *str = NULL; + + switch (functional_type) { + case IFRTYPE_FUNCTIONAL_UNKNOWN: + break; + + case IFRTYPE_FUNCTIONAL_LOOPBACK: + str = "loopback"; + break; + + case IFRTYPE_FUNCTIONAL_WIRED: + str = "wired"; + break; + + case IFRTYPE_FUNCTIONAL_WIFI_INFRA: + str = "wifi"; + break; + + case IFRTYPE_FUNCTIONAL_WIFI_AWDL: + str = "awdl"; + break; + + case IFRTYPE_FUNCTIONAL_CELLULAR: + str = "cellular"; + break; + + case IFRTYPE_FUNCTIONAL_INTCOPROC: + break; + + case IFRTYPE_FUNCTIONAL_COMPANIONLINK: + str = "companionlink"; + break; + + default: + break; + } + return str; +} diff --git a/network_cmds/ifconfig.tproj/ifconfig.h b/network_cmds/ifconfig.tproj/ifconfig.h new file mode 100644 index 0000000..5d4a7e0 --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifconfig.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2009-2018 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1997 Peter Wemm. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the FreeBSD Project + * by Peter Wemm. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * so there! + * + * $FreeBSD: src/sbin/ifconfig/ifconfig.h,v 1.21.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $ + */ + +#define __constructor __attribute__((constructor)) + +struct afswtch; +struct cmd; + +typedef void c_func(const char *cmd, int arg, int s, const struct afswtch *afp); +typedef void c_func2(const char *arg1, const char *arg2, int s, const struct afswtch *afp); +typedef int c_funcv(int argc, char *const *argv, int s, const struct afswtch *afp); + +struct cmd { + const char *c_name; + int c_parameter; +#define NEXTARG 0xffffff /* has following arg */ +#define NEXTARG2 0xfffffe /* has 2 following args */ +#define OPTARG 0xfffffd /* has optional following arg */ +#define VAARGS 0xfffffc /* has variable following args */ + union { + c_func *c_func; + c_func2 *c_func2; + c_funcv *c_funcv; + } c_u; + int c_iscloneop; + struct cmd *c_next; +}; +void cmd_register(struct cmd *); + +typedef void callback_func(int s, void *); +void callback_register(callback_func *, void *); + +/* + * Macros for declaring command functions and initializing entries. + */ +#define DECL_CMD_FUNC(name, cmd, arg) \ + void name(const char *cmd, int arg, int s, const struct afswtch *afp) +#define DECL_CMD_FUNC2(name, arg1, arg2) \ + void name(const char *arg1, const char *arg2, int s, const struct afswtch *afp) + +#define DEF_CMD(name, param, func) { name, param, { .c_func = func } } +#define DEF_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func } } +#define DEF_CMD_OPTARG(name, func) { name, OPTARG, { .c_func = func } } +#define DEF_CMD_ARG2(name, func) { name, NEXTARG2, { .c_func2 = func } } +#define DEF_CMD_VA(name, func) { name, VAARGS, { .c_funcv = func } } +#define DEF_CLONE_CMD(name, param, func) { name, param, { .c_func = func }, 1 } +#define DEF_CLONE_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func }, 1 } + +struct ifaddrs; +struct addrinfo; + +enum { + RIDADDR, + ADDR, + MASK, + DSTADDR, +}; + +struct afswtch { + const char *af_name; /* as given on cmd line, e.g. "inet" */ + short af_af; /* AF_* */ + /* + * Status is handled one of two ways; if there is an + * address associated with the interface then the + * associated address family af_status method is invoked + * with the appropriate addressin info. Otherwise, if + * all possible info is to be displayed and af_other_status + * is defined then it is invoked after all address status + * is presented. + */ + void (*af_status)(int, const struct ifaddrs *); + void (*af_other_status)(int); + /* parse address method */ + void (*af_getaddr)(const char *, int); + /* parse prefix method (IPv6) */ + void (*af_getprefix)(const char *, int); + void (*af_postproc)(int s, const struct afswtch *); + u_long af_difaddr; /* set dst if address ioctl */ + u_long af_aifaddr; /* set if address ioctl */ + void *af_ridreq; /* */ + void *af_addreq; /* */ + struct afswtch *af_next; + + /* XXX doesn't fit model */ + void (*af_status_tunnel)(int); + void (*af_settunnel)(int s, struct addrinfo *srcres, + struct addrinfo *dstres); + + void (*af_setrouter)(int, int); + int (*af_routermode)(int, int, char *const *); +}; +void af_register(struct afswtch *); + +struct option { + const char *opt; + const char *opt_usage; + void (*cb)(const char *arg); + struct option *next; +}; +void opt_register(struct option *); + +extern struct ifreq ifr; +extern char name[IFNAMSIZ]; /* name of interface */ +extern int allmedia; +extern int supmedia; +extern int printkeys; +extern int newaddr; +extern int verbose; +extern int all; + +void setifcap(const char *, int value, int s, const struct afswtch *); + +void Perror(const char *cmd); +void printb(const char *s, unsigned value, const char *bits); + +void ifmaybeload(const char *name); + +typedef void clone_callback_func(int, struct ifreq *); +void clone_setcallback(clone_callback_func *); + +/* + * XXX expose this so modules that neeed to know of any pending + * operations on ifmedia can avoid cmd line ordering confusion. + */ +struct ifmediareq *ifmedia_getstate(int s); diff --git a/network_cmds/ifconfig.tproj/iffake.c b/network_cmds/ifconfig.tproj/iffake.c new file mode 100644 index 0000000..c8065a2 --- /dev/null +++ b/network_cmds/ifconfig.tproj/iffake.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * iffake.c + * - manage fake interfaces that pretend to be e.g. ethernet + */ + +/* + * Modification History: + * + * January 17, 2017 Dieter Siegmund (dieter@apple.com) + * - created + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_fake_var.h> + +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +static void +fake_status(int s) +{ + struct ifdrv ifd; + struct if_fake_request iffr; + + bzero((char *)&ifd, sizeof(ifd)); + bzero((char *)&iffr, sizeof(iffr)); + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = IF_FAKE_G_CMD_GET_PEER; + ifd.ifd_len = sizeof(iffr); + ifd.ifd_data = &iffr; + if (ioctl(s, SIOCGDRVSPEC, &ifd) < 0) { + return; + } + if (iffr.iffr_peer_name[0] == '\0') { + printf("\tpeer: <none>\n"); + } else { + printf("\tpeer: %s\n", iffr.iffr_peer_name); + } + return; +} + +static void +set_peer(int s, const char * operation, const char * val) +{ + struct ifdrv ifd; + struct if_fake_request iffr; + + bzero((char *)&ifd, sizeof(ifd)); + bzero((char *)&iffr, sizeof(iffr)); + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = IF_FAKE_S_CMD_SET_PEER; + ifd.ifd_len = sizeof(iffr); + ifd.ifd_data = &iffr; + if (val != NULL) { + strlcpy(iffr.iffr_peer_name, val, sizeof(iffr.iffr_peer_name)); + } + if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) { + err(1, "SIOCDRVSPEC %s peer", operation); + } + return; +} + +static +DECL_CMD_FUNC(setpeer, val, d) +{ + set_peer(s, "set", val); + return; +} + +static +DECL_CMD_FUNC(unsetpeer, val, d) +{ + set_peer(s, "unset", NULL); + return; +} + +static struct cmd fake_cmds[] = { + DEF_CLONE_CMD_ARG("peer", setpeer), + DEF_CMD_OPTARG("-peer", unsetpeer), +}; +static struct afswtch af_fake = { + .af_name = "af_fake", + .af_af = AF_UNSPEC, + .af_other_status = fake_status, +}; + +static __constructor void +fake_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(fake_cmds); i++) + cmd_register(&fake_cmds[i]); + af_register(&af_fake); +#undef N +} + diff --git a/network_cmds/ifconfig.tproj/ifmedia.c b/network_cmds/ifconfig.tproj/ifmedia.c new file mode 100644 index 0000000..713c136 --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifmedia.c @@ -0,0 +1,872 @@ +/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */ +/* $FreeBSD: src/sbin/ifconfig/ifmedia.c,v 1.25.6.1 2008/11/25 02:59:29 kensmith Exp $ */ + +/* + * Copyright (c) 1997 Jason R. Thorpe. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Jason R. Thorpe. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/route.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +static void domediaopt(const char *, int, int); +static int get_media_subtype(int, const char *); +#ifdef notdef +static int get_media_mode(int, const char *); +#endif +static int get_media_options(int, const char *); +static int lookup_media_word(struct ifmedia_description *, const char *); +static void print_media_word(int, int); +static void print_media_word_ifconfig(int); + +static struct ifmedia_description *get_toptype_desc(int); +static struct ifmedia_type_to_subtype *get_toptype_ttos(int); +static struct ifmedia_description *get_subtype_desc(int, + struct ifmedia_type_to_subtype *ttos); + +static void +media_status(int s) +{ + struct ifmediareq ifmr; + int *media_list, i; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFXMEDIA, (caddr_t)&ifmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFMEDIA. + */ + return; + } + + if (ifmr.ifm_count == 0) { + warnx("%s: no media types?", name); + return; + } + + media_list = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (media_list == NULL) + err(1, "malloc"); + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFXMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFXMEDIA"); + + printf("\tmedia: "); + print_media_word(ifmr.ifm_current, 1); + if (ifmr.ifm_active != ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(ifmr.ifm_active, 0); + putchar(')'); + } + + putchar('\n'); + + if (ifmr.ifm_status & IFM_AVALID) { + printf("\tstatus: "); +#ifdef notdef + switch (IFM_TYPE(ifmr.ifm_active)) { + case IFM_ETHER: + case IFM_ATM: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + break; + + case IFM_FDDI: + case IFM_TOKEN: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("inserted"); + else + printf("no ring"); + break; + + case IFM_IEEE80211: + /* XXX: Different value for adhoc? */ + if (ifmr.ifm_status & IFM_ACTIVE) + printf("associated"); + else + printf("no carrier"); + break; + } +#else + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("inactive"); +#endif + putchar('\n'); + } + + if (ifmr.ifm_count > 0 && supmedia) { + printf("\tsupported media:\n"); + for (i = 0; i < ifmr.ifm_count; i++) { + printf("\t\t"); + print_media_word_ifconfig(media_list[i]); + putchar('\n'); + } + } + + free(media_list); +} + +struct ifmediareq * +ifmedia_getstate(int s) +{ + static struct ifmediareq *ifmr = NULL; + int *mwords; + + if (ifmr == NULL) { + ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq)); + if (ifmr == NULL) + err(1, "malloc"); + + (void) memset(ifmr, 0, sizeof(struct ifmediareq)); + (void) strlcpy(ifmr->ifm_name, name, + sizeof(ifmr->ifm_name)); + + ifmr->ifm_count = 0; + ifmr->ifm_ulist = NULL; + + /* + * We must go through the motions of reading all + * supported media because we need to know both + * the current media type and the top-level type. + */ + + if (ioctl(s, SIOCGIFXMEDIA, (caddr_t)ifmr) < 0) { + err(1, "SIOCGIFXMEDIA"); + } + + if (ifmr->ifm_count == 0) + errx(1, "%s: no media types?", name); + + mwords = (int *)malloc(ifmr->ifm_count * sizeof(int)); + if (mwords == NULL) + err(1, "malloc"); + + ifmr->ifm_ulist = mwords; + if (ioctl(s, SIOCGIFXMEDIA, (caddr_t)ifmr) < 0) + err(1, "SIOCGIFXMEDIA"); + } + + return ifmr; +} + +static void +setifmediacallback(int s, void *arg) +{ + struct ifmediareq *ifmr = (struct ifmediareq *)arg; + static int did_it = 0; + + if (!did_it) { + ifr.ifr_media = ifmr->ifm_current; + if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, "SIOCSIFMEDIA (media)"); + free(ifmr->ifm_ulist); + free(ifmr); + did_it = 1; + } +} + +static void +setmedia(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int subtype; + + ifmr = ifmedia_getstate(s); + + /* + * We are primarily concerned with the top-level type. + * However, "current" may be only IFM_NONE, so we just look + * for the top-level type in the first "supported type" + * entry. + * + * (I'm assuming that all supported media types for a given + * interface will be the same top-level type..) + */ + subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~(IFM_NMASK|IFM_TMASK)) | + IFM_TYPE(ifmr->ifm_ulist[0]) | subtype; + + if ((ifr.ifr_media & IFM_TMASK) == 0) { + ifr.ifr_media &= ~IFM_GMASK; + } + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 0, s); +} + +static void +unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 1, s); +} + +static void +domediaopt(const char *val, int clear, int s) +{ + struct ifmediareq *ifmr; + int options; + + ifmr = ifmedia_getstate(s); + + options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = ifmr->ifm_current; + if (clear) + ifr.ifr_media &= ~options; + else { + if (options & IFM_HDX) { + ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + ifr.ifr_media |= options; + } + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediainst(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int inst; + + ifmr = ifmedia_getstate(s); + + inst = atoi(val); + if (inst < 0 || inst > IFM_INST_MAX) + errx(1, "invalid media instance: %s", val); + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +#ifdef notdef +static void +setmediamode(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int mode; + + ifmr = ifmedia_getstate(s); + + mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} +#endif + +/********************************************************************** + * A good chunk of this is duplicated from sys/net/ifmedia.c + **********************************************************************/ + +static struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_aliases[] = + IFM_SUBTYPE_ETHERNET_ALIASES; + +static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_aliases[] = + IFM_SUBTYPE_TOKENRING_ALIASES; + +static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_aliases[] = + IFM_SUBTYPE_FDDI_ALIASES; + +static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +#ifdef notdef +static struct ifmedia_description ifm_subtype_ieee80211_aliases[] = +IFM_SUBTYPE_IEEE80211_ALIASES; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] = + IFM_SUBTYPE_IEEE80211_MODE_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_atm_aliases[] = + IFM_SUBTYPE_ATM_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; +#endif + +static struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_aliases[] = + IFM_SUBTYPE_SHARED_ALIASES; + +static struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +struct ifmedia_type_to_subtype { + struct { + struct ifmedia_description *desc; + int alias; + } subtypes[5]; + struct { + struct ifmedia_description *desc; + int alias; + } options[3]; + struct { + struct ifmedia_description *desc; + int alias; + } modes[3]; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ethernet_descriptions[0], 0 }, + { &ifm_subtype_ethernet_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ethernet_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_tokenring_descriptions[0], 0 }, + { &ifm_subtype_tokenring_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_tokenring_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_fddi_descriptions[0], 0 }, + { &ifm_subtype_fddi_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_fddi_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +#ifdef __APPLE__ + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 1 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +#else /* __APPLE__ */ +#ifdef notdef + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_subtype_ieee80211_mode_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_mode_aliases[0], 0 }, + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_atm_descriptions[0], 0 }, + { &ifm_subtype_atm_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_atm_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +#endif +#endif /* __APPLE__ */ +}; + +static int +get_media_subtype(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->subtypes[i].desc, val); + if (rval != -1) + return (rval); + } + errx(1, "unknown media subtype: %s", val); + /*NOTREACHED*/ +} + +#ifdef notdef +static int +get_media_mode(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media mode 0x%x", type); + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->modes[i].desc, val); + if (rval != -1) + return (rval); + } + return -1; +} +#endif + +static int +get_media_options(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + char *optlist, *optptr; + int option = 0, i, rval = 0; + + /* We muck with the string, so copy it. */ + optlist = strdup(val); + if (optlist == NULL) + err(1, "strdup"); + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + /* + * Look up the options in the user-provided comma-separated + * list. + */ + optptr = optlist; + for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) { + for (i = 0; ttos->options[i].desc != NULL; i++) { + option = lookup_media_word(ttos->options[i].desc, optptr); + if (option != -1) + break; + } + if (option == 0) + errx(1, "unknown option: %s", optptr); + rval |= option; + } + + free(optlist); + return (rval); +} + +static int +lookup_media_word(struct ifmedia_description *desc, const char *val) +{ + + for (; desc->ifmt_string != NULL; desc++) + if (strcasecmp(desc->ifmt_string, val) == 0) + return (desc->ifmt_word); + + return (-1); +} + +static struct ifmedia_description *get_toptype_desc(int ifmw) +{ + struct ifmedia_description *desc; + + for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return desc; +} + +static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return ttos; +} + +static struct ifmedia_description *get_subtype_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + if (ttos->subtypes[i].alias) + continue; + for (desc = ttos->subtypes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +#ifdef notdef +static struct ifmedia_description *get_mode_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + if (ttos->modes[i].alias) + continue; + for (desc = ttos->modes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_MODE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} +#endif + +static void +print_media_word(int ifmw, int print_toptype) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; +#ifdef notdef + } else if (print_toptype) { + printf("%s", desc->ifmt_string); +#endif + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + +#ifdef notdef + if (print_toptype) + putchar(' '); +#endif + + printf("%s", desc->ifmt_string); + +#ifdef notdef + if (print_toptype) { + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string)) + printf(" mode %s", desc->ifmt_string); + } +#endif + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + printf("%s", seen_option ? ">" : ""); + +#ifdef notdef + if (print_toptype && IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +#endif +} + +static void +print_media_word_ifconfig(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + printf("media %s", desc->ifmt_string); + +#ifdef notdef + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL) + printf(" mode %s", desc->ifmt_string); +#endif + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + printf(" mediaopt %s", desc->ifmt_string); + } + } + } + + if (IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +/********************************************************************** + * ...until here. + **********************************************************************/ + +static struct cmd media_cmds[] = { + DEF_CMD_ARG("media", setmedia), +#ifdef notdef + DEF_CMD_ARG("mode", setmediamode), +#endif + DEF_CMD_ARG("mediaopt", setmediaopt), + DEF_CMD_ARG("-mediaopt",unsetmediaopt), + DEF_CMD_ARG("inst", setmediainst), + DEF_CMD_ARG("instance", setmediainst), +}; +static struct afswtch af_media = { + .af_name = "af_media", + .af_af = AF_UNSPEC, + .af_other_status = media_status, +}; + +static __constructor void +ifmedia_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(media_cmds); i++) + cmd_register(&media_cmds[i]); + af_register(&af_media); +#undef N +} diff --git a/network_cmds/ifconfig.tproj/ifvlan.c b/network_cmds/ifconfig.tproj/ifvlan.c new file mode 100644 index 0000000..9ec0a0a --- /dev/null +++ b/network_cmds/ifconfig.tproj/ifvlan.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_vlan_var.h> +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +#include <sys/cdefs.h> + +#define NOTAG ((u_short) -1) + +static struct vlanreq params = { + .vlr_tag = NOTAG, +}; + +static int +getvlan(int s, struct ifreq *ifr, struct vlanreq *vreq) +{ + bzero((char *)vreq, sizeof(*vreq)); + ifr->ifr_data = (caddr_t)vreq; + + return ioctl(s, SIOCGETVLAN, (caddr_t)ifr); +} + +static void +vlan_status(int s) +{ + struct vlanreq vreq; + + if (getvlan(s, &ifr, &vreq) != -1) + printf("\tvlan: %d parent interface: %s\n", + vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ? + "<none>" : vreq.vlr_parent); +} + +static void +vlan_create(int s, struct ifreq *ifr) +{ + if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { + /* + * One or both parameters were specified, make sure both. + */ + if (params.vlr_tag == NOTAG) + errx(1, "must specify a tag for vlan create"); + if (params.vlr_parent[0] == '\0') + errx(1, "must specify a parent device for vlan create"); + ifr->ifr_data = (caddr_t) ¶ms; + } +#ifdef SIOCIFCREATE2 + if (ioctl(s, SIOCIFCREATE2, ifr) < 0) + err(1, "SIOCIFCREATE2"); +#else + if (ioctl(s, SIOCIFCREATE, ifr) < 0) + err(1, "SIOCIFCREATE"); +#endif +} + +static void +vlan_cb(int s, void *arg) +{ + if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) + errx(1, "both vlan and vlandev must be specified"); +} + +static void +vlan_set(int s, struct ifreq *ifr) +{ + if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { + ifr->ifr_data = (caddr_t) ¶ms; + if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) + err(1, "SIOCSETVLAN"); + } +} + +static +DECL_CMD_FUNC(setvlantag, val, d) +{ + struct vlanreq vreq; + u_long ul; + char *endp; + + ul = strtoul(val, &endp, 0); + if (*endp != '\0') + errx(1, "invalid value for vlan"); + params.vlr_tag = ul; + /* check if the value can be represented in vlr_tag */ + if (params.vlr_tag != ul) + errx(1, "value for vlan out of range"); + + if (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); + else + clone_setcallback(vlan_create); +} + +static +DECL_CMD_FUNC(setvlandev, val, d) +{ + struct vlanreq vreq; + + strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); + + if (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); + else + clone_setcallback(vlan_create); +} + +static +DECL_CMD_FUNC(unsetvlandev, val, d) +{ + struct vlanreq vreq; + + bzero((char *)&vreq, sizeof(struct vlanreq)); + ifr.ifr_data = (caddr_t)&vreq; + + if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1) + err(1, "SIOCGETVLAN"); + + bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); + vreq.vlr_tag = 0; + + if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1) + err(1, "SIOCSETVLAN"); +} + +static struct cmd vlan_cmds[] = { + DEF_CLONE_CMD_ARG("vlan", setvlantag), + DEF_CLONE_CMD_ARG("vlandev", setvlandev), + /* XXX For compatibility. Should become DEF_CMD() some day. */ + DEF_CMD_OPTARG("-vlandev", unsetvlandev), +#ifdef IFCAP_VLAN_MTU + DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), + DEF_CMD("-vlanmtu", -IFCAP_VLAN_MTU, setifcap), +#endif /* IFCAP_VLAN_MTU */ +#ifdef IFCAP_VLAN_HWTAGGING + DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), + DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap), +#endif /* IFCAP_VLAN_HWTAGGING */ +}; +static struct afswtch af_vlan = { + .af_name = "af_vlan", + .af_af = AF_UNSPEC, + .af_other_status = vlan_status, +}; + +static __constructor void +vlan_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(vlan_cmds); i++) + cmd_register(&vlan_cmds[i]); + af_register(&af_vlan); + callback_register(vlan_cb, NULL); +#undef N +} diff --git a/network_cmds/ifconfig.tproj/nexus.c b/network_cmds/ifconfig.tproj/nexus.c new file mode 100644 index 0000000..3df9842 --- /dev/null +++ b/network_cmds/ifconfig.tproj/nexus.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * nexus.c + * - report information about attached nexus + */ + +/* + * Modification History: + * + * April 10, 2017 Dieter Siegmund (dieter@apple.com) + * - created + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_fake_var.h> + +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +static void +nexus_status(int s) +{ + struct if_nexusreq ifnr; + uuid_string_t flowswitch; + uuid_string_t netif; + + if (!verbose) { + return; + } + bzero((char *)&ifnr, sizeof(ifnr)); + strlcpy(ifnr.ifnr_name, ifr.ifr_name, sizeof(ifnr.ifnr_name)); + if (ioctl(s, SIOCGIFNEXUS, &ifnr) < 0) { + return; + } + if (uuid_is_null(ifnr.ifnr_netif)) { + /* technically, this shouldn't happen */ + return; + } + uuid_unparse_upper(ifnr.ifnr_netif, netif); + printf("\tnetif: %s\n", netif); + if (uuid_is_null(ifnr.ifnr_flowswitch) == 0) { + uuid_unparse_upper(ifnr.ifnr_flowswitch, flowswitch); + printf("\tflowswitch: %s\n", flowswitch); + } + return; +} + +static struct afswtch af_fake = { + .af_name = "af_fake", + .af_af = AF_UNSPEC, + .af_other_status = nexus_status, +}; + +static __constructor void +fake_ctor(void) +{ + af_register(&af_fake); +} + diff --git a/network_cmds/ip6addrctl.tproj/ip6addrctl.8 b/network_cmds/ip6addrctl.tproj/ip6addrctl.8 new file mode 100644 index 0000000..59154d4 --- /dev/null +++ b/network_cmds/ip6addrctl.tproj/ip6addrctl.8 @@ -0,0 +1,126 @@ +.\" $KAME: ip6addrctl.8,v 1.3 2003/03/22 05:56:41 jinmei Exp $ +.\" +.\" Copyright (C) 2001 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 25, 2001 +.Dt IP6ADDRCTL 8 +.Os +.\" +.Sh NAME +.Nm ip6addrctl +.Nd configure address selection policy for IPv6 and IPv4 +.\" +.Sh SYNOPSIS +.Nm +.Op Cm show +.Nm +.Cm add +.Ar prefix precedence label +.Nm +.Cm delete +.Ar prefix +.Nm +.Cm flush +.Nm +.Cm install +.Ar configfile +.\" +.Sh DESCRIPTION +The +.Nm +utility manages the policy table of source and destination address +selection for outgoing IPv4 and IPv6 packets. +When +.Nm +is invoked without an argument or with a single argument +.Cm show , +it prints the content of the policy table currently installed in the +kernel. +.Pp +To modify the table, the following operations are available: +.Bl -tag -width indent +.It Cm add Ar prefix precedence label +Add a policy entry. +The +.Ar prefix +argument +is an IPv6 prefix, which is a key for the entry. +An IPv4 prefix should be specified with an IPv6 prefix using an +IPv4-mapped IPv6 address. +The +.Ar precedence +and +.Ar label +arguments +are decimal numbers, which specify the precedence and label values +for the entry, respectively. +This operation should be performed without an existing entry for the +prefix. +.It Cm delete Ar prefix +Delete a policy entry specified by +.Ar prefix , +which should be an IPv6 prefix. +A corresponding entry for the prefix should have already been +installed. +.It Cm flush +Delete all existing policy entries in the kernel. +.It Cm install Ar configfile +Install policy entries from a configuration file named +.Ar configfile . +The configuration file should contain a set of policy entries. +Each entry is specified in a single line which contains an IPv6 prefix, +a decimal precedence value, and a decimal label value, separated with +white space or tab characters. +In the configuration file, lines beginning with the pound-sign +.Pq Ql # +are +comments and are ignored. +.El +.\" +.Sh EXIT STATUS +.Ex -std +.\" +.Sh SEE ALSO +.Rs +.%A "Richard Draves" +.%T "Default Address Selection for IPv6" +.%N RFC 3484 +.Re +.\" +.Sh HISTORY +The +.Nm +utility first appeared in the KAME IPv6 protocol stack kit. +The original command name was +.Nm addrselect , +but it was then renamed to the current one so that the name would +describe its function well. +.\" .Sh BUGS +.\" (to be written) diff --git a/network_cmds/ip6addrctl.tproj/ip6addrctl.c b/network_cmds/ip6addrctl.tproj/ip6addrctl.c new file mode 100644 index 0000000..400412a --- /dev/null +++ b/network_cmds/ip6addrctl.tproj/ip6addrctl.c @@ -0,0 +1,467 @@ +/* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */ + +/* + * Copyright (C) 2001 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet6/in6_var.h> + +#include <stdlib.h> +#include <netdb.h> +#include <stdio.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> +#include <err.h> + +static char *configfile; + +struct policyqueue { + TAILQ_ENTRY(policyqueue) pc_entry; + struct in6_addrpolicy pc_policy; +}; +TAILQ_HEAD(policyhead, policyqueue); +struct policyhead policyhead; + +static void usage __P((void)); +static void get_policy __P((void)); +static void dump_policy __P((void)); +static int mask2plen __P((struct sockaddr_in6 *)); +static int parse_prefix __P((const char *, struct in6_addrpolicy *)); +static void make_policy_fromfile __P((char *)); +static void plen2mask __P((struct sockaddr_in6 *, int)); +static void set_policy __P((void)); +static void add_policy __P((char *, char *, char *)); +static void delete_policy __P((char *)); +static void flush_policy __P(()); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + TAILQ_INIT(&policyhead); + + if (argc == 1 || strcasecmp(argv[1], "show") == 0) { + get_policy(); + dump_policy(); + } else if (strcasecmp(argv[1], "add") == 0) { + if (argc < 5) + usage(); + add_policy(argv[2], argv[3], argv[4]); + } else if (strcasecmp(argv[1], "delete") == 0) { + if (argc < 3) + usage(); + delete_policy(argv[2]); + } else if (strcasecmp(argv[1], "flush") == 0) { + get_policy(); + flush_policy(); + } else if (strcasecmp(argv[1], "install") == 0) { + if (argc < 3) + usage(); + configfile = argv[2]; + make_policy_fromfile(configfile); + set_policy(); + } else + usage(); + + exit(0); +} + +static void +get_policy() +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; + size_t l; + char *buf; + struct in6_addrpolicy *pol, *ep; + + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { + err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); + /* NOTREACHED */ + } + if (l == 0) { + printf("no source-address-selection policy is installed\n"); + return; + } + if ((buf = malloc(l)) == NULL) { + errx(1, "malloc failed"); + /* NOTREACHED */ + } + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { + err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); + /* NOTREACHED */ + } + + ep = (struct in6_addrpolicy *)(buf + l); + for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { + struct policyqueue *new; + + if ((new = malloc(sizeof(*new))) == NULL) + errx(1, "malloc failed\n"); + new->pc_policy = *pol; + TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); + } + + free(buf); +} + +static void +dump_policy() +{ + size_t addrlen; + char addrbuf[NI_MAXHOST]; + struct in6_addrpolicy *pol; + struct policyqueue *ent; + int plen, first = 1; + + for (ent = TAILQ_FIRST(&policyhead); ent; + ent = TAILQ_NEXT(ent, pc_entry)) { + pol = &ent->pc_policy; + if (first) { + printf("%-30s %5s %5s %8s\n", + "Prefix", "Prec", "Label", "Use"); + first = 0; + } + + if ((getnameinfo((struct sockaddr *)&pol->addr, + sizeof(pol->addr), addrbuf, sizeof(addrbuf), + NULL, 0, NI_NUMERICHOST))) { + warnx("getnameinfo for prefix address failed"); + continue; + } + if ((plen = mask2plen(&pol->addrmask)) < 0) { + warnx("invalid address mask"); + continue; + } + addrlen = strlen(addrbuf); + if (addrlen + sizeof("/128") < sizeof(addrbuf)) { + snprintf(&addrbuf[addrlen], + sizeof(addrbuf) - addrlen - 1, + "/%d", plen); + printf("%-30s", addrbuf); + } else /* XXX */ + printf("%s/%d", addrbuf, plen); + printf(" %5d %5d %8llu\n", pol->preced, pol->label, + (unsigned long long)pol->use); + } +} + +#define SKIP_WHITE(p, emptyok) \ + do { \ + while((*(p) == ' ' || *(p) == '\t')) \ + (p)++; \ + if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \ + goto bad; \ + } while (0); +#define SKIP_WORD(p) \ + do { \ + while(*(p) != ' ' && *(p) != '\t') \ + (p)++; \ + if (*(p) == '\0' || *(p) == '\n') \ + goto bad; \ + } while (0); + +static void +make_policy_fromfile(conf) + char *conf; +{ + char line[_POSIX2_LINE_MAX], *cp; + char *addrstr; + FILE *fp; + int count = 0; + struct in6_addrpolicy pol0; + struct policyqueue *new; + + if ((fp = fopen(conf, "r")) == NULL) + err(1, "fopen: %s", conf); + + while(fgets(line, sizeof(line), fp)) { + count++; + cp = line; + + memset(&pol0, 0, sizeof(pol0)); + + /* get prefix */ + SKIP_WHITE(cp, 1); + if (*cp == '\n') /* empty line */ + continue; + if (*cp == '#') + continue; + addrstr = cp; + if (parse_prefix((const char *)addrstr, &pol0)) + goto bad; + + /* get precedence value */ + SKIP_WORD(cp); + SKIP_WHITE(cp, 0); + pol0.preced = atoi(cp); + + /* get label */ + SKIP_WORD(cp); + SKIP_WHITE(cp, 0); + pol0.label = atoi(cp); + + /* parse succeeded. make a control buffer entry. */ + if ((new = malloc(sizeof(*new))) == NULL) + errx(1, "malloc failed\n"); + memset(new, 0, sizeof(*new)); + new->pc_policy = pol0; + TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); + } + + fclose(fp); + return; + + bad: + errx(1, "parse failed at line %d", count); + /* NOTREACHED */ +} + +static int +parse_prefix(prefix0, pol) + const char *prefix0; + struct in6_addrpolicy *pol; +{ + int e = 0, plen; + char *prefix, *plenstr; + struct addrinfo hints, *res; + + if ((prefix = strdup(prefix0)) == NULL) + errx(1, "strdup failed"); + + if ((plenstr = strchr(prefix, '/')) == NULL) { + e = -1; + goto end; + } + *plenstr = '\0'; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_INET6; + + if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) { + warnx("getaddrinfo failed for %s: %s", prefix, + gai_strerror(e)); + goto end; + } + memcpy(&pol->addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + plen = atoi(plenstr + 1); + if (plen < 0 || plen > 128) { + warnx("invalid prefix length: %d", plen); + e = -1; + goto end; + } + plen2mask(&pol->addrmask, plen); + + end: + free(prefix); + return(e); +} + +static void +plen2mask(mask, plen) + struct sockaddr_in6 *mask; + int plen; +{ + u_char *cp = (unsigned char *)&mask->sin6_addr; + + memset(mask, 0, sizeof(*mask)); + mask->sin6_family = AF_INET6; /* just in case */ + mask->sin6_len = sizeof(*mask); + + for(; plen >= 8; plen -= 8) + *cp++ = 0xff; + if (plen > 0) + *cp = (0xff << (8 - plen)); +} + +static void +set_policy() +{ + struct policyqueue *ent; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + + for (ent = TAILQ_FIRST(&policyhead); ent; + ent = TAILQ_NEXT(ent, pc_entry)) { + if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy)) + warn("ioctl(SIOCAADDRCTL_POLICY)"); + } + + close(s); +} + +static int +mask2plen(mask) + struct sockaddr_in6 *mask; +{ + int masklen, final = 0; + u_char *p, *lim; + + masklen = 0; + lim = (u_char *)(mask + 1); + for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) { + if (final && *p) { + goto bad; + } + + switch (*p & 0xff) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + final++; + break; + case 0xfc: + masklen += 6; + final++; + break; + case 0xf8: + masklen += 5; + final++; + break; + case 0xf0: + masklen += 4; + final++; + break; + case 0xe0: + masklen += 3; + final++; + break; + case 0xc0: + masklen += 2; + final++; + break; + case 0x80: + masklen += 1; + final++; + break; + case 0x00: + final++; + break; + default: + goto bad; + break; + } + } + return(masklen); + + bad: + return(-1); +} + +static void +add_policy(prefix, prec, label) + char *prefix, *prec, *label; +{ + struct in6_addrpolicy p; + int s; + + memset(&p, 0, sizeof(p)); + + if (parse_prefix((const char *)prefix, &p)) + errx(1, "bad prefix: %s", prefix); + p.preced = atoi(prec); + p.label = atoi(label); + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + if (ioctl(s, SIOCAADDRCTL_POLICY, &p)) + err(1, "ioctl(SIOCAADDRCTL_POLICY)"); + + close(s); +} + +static void +delete_policy(prefix) + char *prefix; +{ + struct in6_addrpolicy p; + int s; + + memset(&p, 0, sizeof(p)); + + if (parse_prefix((const char *)prefix, &p)) + errx(1, "bad prefix: %s", prefix); + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + if (ioctl(s, SIOCDADDRCTL_POLICY, &p)) + err(1, "ioctl(SIOCDADDRCTL_POLICY)"); + + close(s); +} + +static void +flush_policy() +{ + struct policyqueue *ent; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + + for (ent = TAILQ_FIRST(&policyhead); ent; + ent = TAILQ_NEXT(ent, pc_entry)) { + if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy)) + warn("ioctl(SIOCDADDRCTL_POLICY)"); + } + + close(s); +} + +static void +usage() +{ + fprintf(stderr, "usage: ip6addrctl [show]\n"); + fprintf(stderr, " ip6addrctl add " + "<prefix> <precedence> <label>\n"); + fprintf(stderr, " ip6addrctl delete <prefix>\n"); + fprintf(stderr, " ip6addrctl flush\n"); + fprintf(stderr, " ip6addrctl install <configfile>\n"); + + exit(1); +} diff --git a/network_cmds/ip6addrctl.tproj/ip6addrctl.conf b/network_cmds/ip6addrctl.tproj/ip6addrctl.conf new file mode 100644 index 0000000..a2b3759 --- /dev/null +++ b/network_cmds/ip6addrctl.tproj/ip6addrctl.conf @@ -0,0 +1,12 @@ +# default policy table based on RFC 3484. +# usage: ip6addrctl install path_to_this_file +# +# $FreeBSD$ +# +#Format: +#Prefix Precedence Label +::1/128 50 0 +::/0 40 1 +2002::/16 30 2 +::/96 20 3 +::ffff:0:0/96 10 4 diff --git a/network_cmds/kdumpd.tproj/com.apple.kdumpd.plist b/network_cmds/kdumpd.tproj/com.apple.kdumpd.plist new file mode 100644 index 0000000..3b09ade --- /dev/null +++ b/network_cmds/kdumpd.tproj/com.apple.kdumpd.plist @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Disabled</key> + <true/> + <key>InitGroups</key> + <true/> + <key>Label</key> + <string>com.apple.kdumpd</string> + <key>ProgramArguments</key> + <array> + <string>/usr/libexec/kdumpd</string> + <string>/var/tmp/PanicDumps</string> + </array> + <key>Sockets</key> + <dict> + <key>Listener</key> + <dict> + <key>SockServiceName</key> + <string>1069</string> + <key>SockType</key> + <string>dgram</string> + </dict> + </dict> + <key>UserName</key> + <string>nobody</string> + <key>Umask</key> + <integer>7</integer> + <key>inetdCompatibility</key> + <dict> + <key>Wait</key> + <true/> + </dict> +</dict> +</plist> diff --git a/network_cmds/kdumpd.tproj/kdump.h b/network_cmds/kdumpd.tproj/kdump.h new file mode 100644 index 0000000..9536946 --- /dev/null +++ b/network_cmds/kdumpd.tproj/kdump.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kdump.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _KDUMP_H_ +#define _KDUMP_H_ +#include <netinet/ip_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> + +/* Mac OS X kernel core dump server, based on the BSD trivial file + * transfer protocol server (FreeBSD distribution), with several + * modifications. This server is *not* compatible with tftp, as the + * protocol has changed considerably. + */ + +#define SEGSIZE 512 /* data segment size */ +#define MAXIMUM_KDP_PKTSIZE (16384) +/* + * Packet types. + */ +#define RRQ 1 /* read request */ +#define WRQ 2 /* write request */ +#define DATA 3 /* data packet */ +#define ACK 4 /* acknowledgement */ +#define ERROR 5 /* error code */ +#define KDP_SEEK 6 /* Seek to specified offset */ +#define KDP_EOF 7 /* end of file */ + +struct kdumphdr { + short th_opcode; /* packet type */ + union { + unsigned int tu_block; /* block # */ + unsigned int tu_code; /* error code */ + char tu_stuff[1]; /* request packet stuff */ + } th_u; + char th_data[0]; /* data or error string */ +}__attribute__((packed)); + +#define th_block th_u.tu_block +#define th_code th_u.tu_code +#define th_stuff th_u.tu_stuff +#define th_msg th_data + +/* + * Error codes. + */ +#define EUNDEF 0 /* not defined */ +#define ENOTFOUND 1 /* file not found */ +#define EACCESS 2 /* access violation */ +#define ENOSPACE 3 /* disk full or allocation exceeded */ +#define EBADOP 4 /* illegal KDUMP operation */ +#define EBADID 5 /* unknown transfer ID */ +#define EEXISTS 6 /* file already exists */ +#define ENOUSER 7 /* no such user */ + +#define DEBUG 0 +#define WRITE_DEBUG 0 +#define KDUMPD_DEBUG_LEVEL LOG_ALERT +#define KDP_LARGE_CRASHDUMP_PKT_SIZE (1440 - sizeof(struct udpiphdr)) + +#endif diff --git a/network_cmds/kdumpd.tproj/kdumpd.8 b/network_cmds/kdumpd.tproj/kdumpd.8 new file mode 100755 index 0000000..8ce4a8e --- /dev/null +++ b/network_cmds/kdumpd.tproj/kdumpd.8 @@ -0,0 +1,83 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD: src/libexec/tftpd/tftpd.8,v 1.15 2001/07/15 07:53:42 dd Exp $ +.\" +.Dd June 10, 2020 +.Dt KDUMPD 8 +.Os +.Sh NAME +.Nm kdumpd +.Nd Mac OS X remote kernel core dump server +.Sh SYNOPSIS +.Nm /usr/libexec/kdumpd +.Op Ar directory +.Sh DESCRIPTION +.Nm Kdumpd +is a server which receives +kernel states in the form of +a core dump from a remote +Mac OS X machine. +The +.Tn kdumpd +server operates +on UDP port 1069, although this +may be configurable in the future. +The server should be started by +.Xr launchctl 1 . +.Pp +The server should have the user ID +with the lowest possible privilege, +usually the user "nobody". +.Pp +By default the server stores kernel cores +in the directory +.Pa /var/tmp/PanicDumps . +The directory needs to already exist for kdumpd +to save core dumps. +.Pp +The server returns an EEXIST error +to the remote kernel if it receives a +request for an existing file - i.e. +only new files can be created. The server +also disallows path specifications in the +incoming file name. +.Sh HISTORY +The +.Nm +command is based on Berkeley +.Xr tftpd 8 , +by way of FreeBSD, with several modifications. +.Sh SEE ALSO +.Xr launchd 8 , +.Xr launchctl 1 , +.Xr launchd.plist 5 diff --git a/network_cmds/kdumpd.tproj/kdumpd.c b/network_cmds/kdumpd.tproj/kdumpd.c new file mode 100644 index 0000000..61762fe --- /dev/null +++ b/network_cmds/kdumpd.tproj/kdumpd.c @@ -0,0 +1,726 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* Mac OS X kernel core dump server, based on the BSD trivial file + * transfer protocol server (FreeBSD distribution), with several + * modifications. This server is *not* compatible with tftp, as the + * protocol has changed considerably. + */ + +/* + * Based on the trivial file transfer protocol server. + * + * The original version included many modifications by Jim Guyton + * <guyton@rand-unix>. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <netinet/in.h> +#include "kdump.h" +#include <arpa/inet.h> + +#include <assert.h> +#include <stdint.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <libkern/OSByteOrder.h> + +#include "kdumpsubs.h" + +#define DEFAULT_KDUMPD_PORTNO (1069) +#define TIMEOUT 2 + +int peer; +int rexmtval = TIMEOUT; +int maxtimeout = 25 * TIMEOUT; + +#define PKTSIZE SEGSIZE+6 + +char buf[MAXIMUM_KDP_PKTSIZE]; +char ackbuf[MAXIMUM_KDP_PKTSIZE]; +struct sockaddr_in from; +socklen_t fromlen; + +void kdump __P((struct kdumphdr *, int)); + +/* + * Null-terminated directory prefix list for absolute pathname requests and + * search list for relative pathname requests. + * + * MAXDIRS should be at least as large as the number of arguments that + * inetd allows (currently 20). + */ +#define MAXDIRS 20 +static struct dirlist { + char *name; + int len; +} dirs[MAXDIRS+1]; +static int suppress_naks; +static int logging = 1; +static int ipchroot; +static int server_mode = 1; + +static char *errtomsg __P((int)); +static void nak __P((int)); +static char * __P(verifyhost(struct sockaddr_in *)); +uint32_t kdp_crashdump_pkt_size = (SEGSIZE + (sizeof(struct kdumphdr))); +uint32_t kdp_crashdump_seg_size = SEGSIZE; + +#define KDP_FEATURE_MASK_STRING "features" +enum {KDP_FEATURE_LARGE_CRASHDUMPS = 1, KDP_FEATURE_LARGE_PKT_SIZE = 2}; + +uint32_t kdp_crashdump_feature_mask; +uint32_t kdp_feature_large_crashdumps, kdp_feature_large_packets; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct kdumphdr *tp; + register int n; + int ch, on; + struct sockaddr_in sin; + char *chroot_dir = NULL; + struct passwd *nobody; + char *chuser = "nobody"; + + openlog("kdumpd", LOG_PID | LOG_NDELAY, LOG_FTP); + while ((ch = getopt(argc, argv, "cClns:u:w")) != -1) { + switch (ch) { + case 'c': + ipchroot = 1; + break; + case 'C': + ipchroot = 2; + break; + case 'l': + logging = 1; + break; + case 'n': + suppress_naks = 1; + break; + case 's': + chroot_dir = optarg; + break; + case 'u': + chuser = optarg; + break; + case 'w': + server_mode = 0; + break; + default: + syslog(LOG_WARNING, "ignoring unknown option -%c", ch); + } + } + + if (optind < argc) { + struct dirlist *dirp; + + /* Get list of directory prefixes. Skip relative pathnames. */ + for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; + optind++) { + if (argv[optind][0] == '/') { + dirp->name = argv[optind]; + dirp->len = strlen(dirp->name); + dirp++; + } + } + } + else if (chroot_dir) { + dirs->name = "/"; + dirs->len = 1; + } + if (ipchroot && chroot_dir == NULL) { + syslog(LOG_ERR, "-c requires -s"); + exit(1); + } + + /* If we are not in server mode, skip the whole 'inetd' logic below. */ + if (server_mode) { + on = 1; + if (ioctl(0, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m"); + exit(1); + } + fromlen = sizeof (from); + n = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + syslog(LOG_ERR, "recvfrom: %m"); + exit(1); + } + /* + * Now that we have read the message out of the UDP + * socket, we fork and exit. Thus, inetd will go back + * to listening to the kdump port, and the next request + * to come in will start up a new instance of kdumpd. + * + * We do this so that inetd can run kdumpd in "wait" mode. + * The problem with kdumpd running in "nowait" mode is that + * inetd may get one or more successful "selects" on the + * kdump port before we do our receive, so more than one + * instance of kdumpd may be started up. Worse, if kdumpd + * breaks before doing the above "recvfrom", inetd would + * spawn endless instances, clogging the system. + */ + { + int pid; + int i; + socklen_t j; + + for (i = 1; i < 20; i++) { + pid = fork(); + if (pid < 0) { + sleep(i); + /* + * flush out to most recently sent request. + * + * This may drop some requests, but those + * will be resent by the clients when + * they timeout. The positive effect of + * this flush is to (try to) prevent more + * than one kdumpd being started up to service + * a single request from a single client. + */ + j = sizeof from; + i = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &j); + if (i > 0) { + n = i; + fromlen = j; + } + } else { + break; + } + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + exit(1); + } else if (pid != 0) { + exit(0); + } + } + } + + /* + * Since we exit here, we should do that only after the above + * recvfrom to keep inetd from constantly forking should there + * be a problem. See the above comment about system clogging. + */ + if (chroot_dir) { + if (ipchroot) { + char tempchroot[MAXPATHLEN]; + char *tempaddr; + struct stat sb; + int statret; + + tempaddr = inet_ntoa(from.sin_addr); + snprintf(tempchroot, sizeof(tempchroot), "%s/%s", chroot_dir, tempaddr); + statret = stat(tempchroot, &sb); + if (((sb.st_mode & S_IFMT ) == S_IFDIR) && + (statret == 0 || (statret == -1 && ipchroot == 1))) + chroot_dir = tempchroot; + } + /* Must get this before chroot because /etc might go away */ + if ((nobody = getpwnam(chuser)) == NULL) { + syslog(LOG_ERR, "%s: no such user", chuser); + exit(1); + } + if (chroot(chroot_dir)) { + syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); + exit(1); + } + chdir( "/" ); + setuid(nobody->pw_uid); + } else if (0 != chdir(dirs->name)) { + syslog(LOG_ERR, "chdir%s: %m", dirs->name); + } + + from.sin_family = AF_INET; + alarm(0); + close(0); + close(1); + peer = socket(AF_INET, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(1); + } + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + + if (!server_mode) { + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons((uint16_t) DEFAULT_KDUMPD_PORTNO); + } + + if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + syslog(LOG_ERR, "bind: %m"); + exit(1); + } + + if (!server_mode) { + /* + * Wait for an incoming message from a remote peer, note that we need to + * populate n since kdump() expect the first message to be in buf + * already. + */ + socklen_t slen = sizeof(from); + n = recvfrom(peer, buf, sizeof(buf), 0, + (struct sockaddr *) &from, &slen); + if (n <= 0) { + syslog(LOG_ERR, "recvfrom: %m"); + exit(1); + } + } + + if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { + syslog(LOG_ERR, "connect: %m"); + exit(1); + } + tp = (struct kdumphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == WRQ) + kdump(tp, n); + exit(1); +} + +struct formats; +int validate_access __P((char **, int)); + +void recvfile __P((struct formats *)); + +struct formats { + char *f_mode; + int (*f_validate) __P((char **, int)); + + void (*f_recv) __P((struct formats *)); + int f_convert; +} formats[] = { + { "netascii", validate_access, recvfile, 1 }, + { "octet", validate_access, recvfile, 0 }, + { 0 } +}; + +/* + * Handle initial connection protocol. + */ +void +kdump(tp, size) + struct kdumphdr *tp; + int size; +{ + register char *cp; + int first = 1, ecode; + register struct formats *pf; + char *filename, *mode = NULL; + + filename = cp = tp->th_stuff; +again: + while (cp < buf + size) { + if (*cp == '\0') + break; + cp++; + } + if (*cp != '\0') { + nak(EBADOP); + exit(1); + } + if (first) { + mode = ++cp; + first = 0; + goto again; + } + for (cp = mode; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + + cp++; + if (strncmp(KDP_FEATURE_MASK_STRING, cp, sizeof(KDP_FEATURE_MASK_STRING)) == 0) { + kdp_crashdump_feature_mask = ntohl(*(uint32_t *) (cp + sizeof(KDP_FEATURE_MASK_STRING))); + kdp_feature_large_crashdumps = kdp_crashdump_feature_mask & KDP_FEATURE_LARGE_CRASHDUMPS; + kdp_feature_large_packets = kdp_crashdump_feature_mask & KDP_FEATURE_LARGE_PKT_SIZE; + + if (kdp_feature_large_packets) { + kdp_crashdump_pkt_size = KDP_LARGE_CRASHDUMP_PKT_SIZE; + kdp_crashdump_seg_size = kdp_crashdump_pkt_size - sizeof(struct kdumphdr); + } + syslog(KDUMPD_DEBUG_LEVEL, "Received feature mask %s:0x%x", cp, kdp_crashdump_feature_mask); + } else + syslog(KDUMPD_DEBUG_LEVEL, "Unable to locate feature mask, mode: %s", mode); + + for (pf = formats; pf->f_mode; pf++) + if (strcmp(pf->f_mode, mode) == 0) + break; + if (pf->f_mode == 0) { + nak(EBADOP); + exit(1); + } + ecode = (*pf->f_validate)(&filename, tp->th_opcode); + if (logging) { + syslog(KDUMPD_DEBUG_LEVEL, "%s: %s request for %s: %s", verifyhost(&from), + tp->th_opcode == WRQ ? "write" : "read", + filename, errtomsg(ecode)); + } + if (ecode) { + /* + * Avoid storms of naks to a RRQ broadcast for a relative + * bootfile pathname from a diskless Sun. + */ + if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) + exit(0); + nak(ecode); + exit(1); + } + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf); + + exit(0); +} + + +FILE *file; + +/* + * Validate file access. We only allow storage of files that do not already + * exist, and that do not include directory specifiers in their pathnames. + * This is because kernel coredump filenames should always be of the form + * "core-version-IP as dotted quad-random string" as in : + * core-custom-17.202.40.204-a75b4eec + * The file is written to the directory supplied as the first argument + * in inetd.conf + */ + +int +validate_access(char **filep, int mode) +{ + struct stat stbuf; + int fd; + char *filename = *filep; + static char pathname[MAXPATHLEN]; + + if (strstr(filename, "/") || strstr(filename, "..")) + return (EACCESS); + + snprintf(pathname, sizeof(pathname), "./%s", filename); + + if (0 == stat(pathname, &stbuf)) + return (EEXIST); + + if (errno != ENOENT) + return (errno); + + + fd = open(filename, O_RDWR|O_CREAT|O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + if (fd < 0) + return (errno + 100); + + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) { + return errno+100; + } + + return (0); +} + +int timeout; +jmp_buf timeoutbuf; + +void +timer() +{ + + timeout += rexmtval; + if (timeout >= maxtimeout) + { + longjmp(timeoutbuf, 2); + } + longjmp(timeoutbuf, 1); +} + +void +justquit() +{ + exit(0); +} + +/* + * Receive a file. + */ +void +recvfile(pf) + struct formats *pf; +{ + struct kdumphdr *dp, *w_init(); + register struct kdumphdr *ap; /* ack buffer */ + register int n, size; + volatile unsigned int block; + volatile unsigned int jmpval = 0; + + signal(SIGALRM, timer); + dp = w_init(); + ap = (struct kdumphdr *)ackbuf; + block = 0; + do { +send_seek_ack: timeout = 0; + if (block == 0) + ap->th_opcode = htons((u_short)ACK | ((kdp_feature_large_crashdumps | kdp_feature_large_packets) << 8)); + else + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htonl((unsigned int)block); + block++; + jmpval = setjmp(timeoutbuf); + if (2 == jmpval) + { + syslog (LOG_ERR, "Timing out and flushing file to disk"); + goto flushfile; + } +send_ack: + if (send(peer, ackbuf, 6 , 0) != 6) { + syslog(LOG_ERR, "write: %m"); + goto abort; + } + write_behind(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, dp, kdp_crashdump_pkt_size, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "read: %m"); + goto abort; + } + dp->th_opcode = ntohs((u_short)dp->th_opcode); + dp->th_block = ntohl((unsigned int)dp->th_block); +#if DEBUG + syslog(KDUMPD_DEBUG_LEVEL, "Received packet type %u, block %u\n", (unsigned)dp->th_opcode, (unsigned)dp->th_block); +#endif + + if (dp->th_opcode == ERROR) + goto abort; + + if (dp->th_opcode == KDP_EOF) + { + syslog (LOG_ERR, "Received last panic dump packet"); + goto final_ack; + } + if (dp->th_opcode == KDP_SEEK) + { + if (dp->th_block == block) + { + off_t crashdump_offset = 0; + unsigned int tempoff = 0; + + if (kdp_feature_large_crashdumps) { + crashdump_offset = OSSwapBigToHostInt64((*(uint64_t *)dp->th_data)); + } + else { + bcopy (dp->th_data, &tempoff, sizeof(unsigned int)); + crashdump_offset = ntohl(tempoff); + } + +#if DEBUG + syslog(KDUMPD_DEBUG_LEVEL, "Seeking to offset 0x%llx\n", crashdump_offset); +#endif + errno = 0; + lseek(fileno (file), crashdump_offset, SEEK_SET); + if (errno) + syslog (LOG_ERR, "lseek: %m"); + + goto send_seek_ack; + } + (void) synchnet(peer); + if (dp->th_block == (block-1)) + { + syslog (LOG_DAEMON|LOG_ERR, "Retransmitting seek ack - current block %u, received block %u", block, dp->th_block); + goto send_ack; /* rexmit */ + } + } + + if (dp->th_opcode == DATA) { + if (dp->th_block == block) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (dp->th_block == (block-1)) + { + syslog (LOG_DAEMON|LOG_ERR, "Retransmitting ack - current block %u, received block %u", block, dp->th_block); + goto send_ack; /* rexmit */ + } + else + syslog (LOG_DAEMON|LOG_ERR, "Not retransmitting ack - current block %u, received block %u", block, dp->th_block); + } + } +#if DEBUG + syslog(KDUMPD_DEBUG_LEVEL, "Writing block sized %u, current offset 0x%llx\n", n - 6, ftello(file)); +#endif + size = writeit(file, &dp, n - 6, pf->f_convert); + if (size != (n-6)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (dp->th_opcode != KDP_EOF); + +final_ack: + ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ + ap->th_block = htonl((unsigned int) (block)); + (void) send(peer, ackbuf, 6, 0); +flushfile: + write_behind(file, pf->f_convert); + (void) fclose(file); /* close data file */ + syslog (LOG_ERR, "file closed, sending final ACK\n"); + + signal(SIGALRM, justquit); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 6 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 6, 0); /* resend final ack */ + } +abort: + return; +} + +/* update if needed, when adding new errmsgs */ +#define MAXERRMSGLEN 40 + +struct errmsg { + int e_code; + char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal KDUMP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { -1, 0 } +}; + +static char * +errtomsg(error) + int error; +{ + static char buf[20]; + register struct errmsg *pe; + if (error == 0) + return "success"; + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + return pe->e_msg; + snprintf(buf, sizeof(buf), "error %d", error); + return buf; +} + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard KDUMP codes, or a UNIX errno + * offset by 100. + */ +static void +nak(error) + int error; +{ + register struct kdumphdr *tp; + int length; + register struct errmsg *pe; + + tp = (struct kdumphdr *)buf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((unsigned int)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + if (strlen(pe->e_msg) > MAXERRMSGLEN) { + syslog(LOG_ERR, "nak: error msg too long"); + return; + } + + strlcpy(tp->th_msg, pe->e_msg, MAXERRMSGLEN); + length = strlen(pe->e_msg); + tp->th_msg[length] = '\0'; + length += 5; + if (send(peer, buf, length, 0) != length) + syslog(LOG_ERR, "nak: %m"); + + return; +} + +static char * +verifyhost(fromp) + struct sockaddr_in *fromp; +{ + struct hostent *hp; + + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(fromp->sin_addr), + fromp->sin_family); + if(hp) + return hp->h_name; + else + return inet_ntoa(fromp->sin_addr); +} diff --git a/network_cmds/kdumpd.tproj/kdumpsubs.c b/network_cmds/kdumpd.tproj/kdumpsubs.c new file mode 100644 index 0000000..283ca62 --- /dev/null +++ b/network_cmds/kdumpd.tproj/kdumpsubs.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Simple minded read-ahead/write-behind subroutines for tftp user and + server. Written originally with multiple buffers in mind, but current + implementation has two buffer logic wired in. + + Todo: add some sort of final error check so when the write-buffer + is finally flushed, the caller can detect if the disk filled up + (or had an i/o error) and return a nak to the other side. + + Jim Guyton 10/85 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif +#include <netinet/in.h> +#include "kdump.h" + +#include <stdio.h> +#include <unistd.h> +#include <syslog.h> + +#include "kdumpsubs.h" + +#define PKTSIZE SEGSIZE+6 /* should be moved to kdump.h */ + +struct bf { + int counter; /* size of data in buffer, or flag */ + char buf[MAXIMUM_KDP_PKTSIZE]; /* room for data packet */ +} bfs[2]; + + /* Values for bf.counter */ +#define BF_ALLOC -3 /* alloc'd but not yet filled */ +#define BF_FREE -2 /* free */ +/* [-1 .. SEGSIZE] = size of data in the data buffer */ + +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ + + /* control flags for crlf conversions */ +int newline = 0; /* fillbuf: in middle of newline expansion */ +int prevchar = -1; /* putbuf: previous char (cr check) */ + +static struct kdumphdr *rw_init __P ((int)); + +struct kdumphdr *w_init() { return rw_init(0); } /* write-behind */ +struct kdumphdr *r_init() { return rw_init(1); } /* read-ahead */ + +extern uint32_t kdp_crashdump_pkt_size; +extern uint32_t kdp_crashdump_seg_size; + +/* init for either read-ahead or write-behind */ +/* zero for write-behind, one for read-head */ +static struct kdumphdr * +rw_init(int x) +{ + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return (struct kdumphdr *)bfs[0].buf; +} + + +/* Have emptied current buffer by sending to net and getting ack. + Free it and return next buffer filled with data. + */ +/* if true, convert to ascii */ +/* file opened for read */ + +/* int */ +/* readit(FILE *file, struct kdumphdr **dpp, int convert) */ +/* { */ +/* struct bf *b; */ + +/* bfs[current].counter = BF_FREE; /\* free old one *\/ */ +/* current = !current; /\* "incr" current *\/ */ + +/* b = &bfs[current]; /\* look at new buffer *\/ */ +/* if (b->counter == BF_FREE) /\* if it's empty *\/ */ +/* read_ahead(file, convert); /\* fill it *\/ */ +/* /\* assert(b->counter != BF_FREE);*\//\* check *\/ */ +/* *dpp = (struct kdumphdr *)b->buf; /\* set caller's ptr *\/ */ +/* return b->counter; */ +/* } */ + +/* + * fill the input buffer, doing ascii conversions if requested + * conversions are lf -> cr,lf and cr -> cr, nul + */ +/* FILE *file; file opened for read */ +/* int convert; if true, convert to ascii */ +void +read_ahead(FILE *file, int convert) +{ + register int i; + register char *p; + register int c; + struct bf *b; + struct kdumphdr *dp; + + b = &bfs[nextone]; /* look at "next" buffer */ + if (b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ + + dp = (struct kdumphdr *)b->buf; + + if (convert == 0) { + b->counter = read(fileno(file), dp->th_data, kdp_crashdump_seg_size); + return; + } + + p = dp->th_data; + for (i = 0 ; i < kdp_crashdump_seg_size; i++) { + if (newline) { + if (prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else c = '\0'; /* cr to cr,nul */ + newline = 0; + } + else { + c = getc(file); + if (c == EOF) break; + if (c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = c; + } + b->counter = (int)(p - dp->th_data); +} + +/* Update count associated with the buffer, get new buffer + from the queue. Calls write_behind only if next buffer not + available. + */ +int +writeit(FILE *file, struct kdumphdr **dpp, int ct, int convert) +{ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if (bfs[current].counter != BF_FREE) /* if not free */ + (void)write_behind(file, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = (struct kdumphdr *)bfs[current].buf; + return ct; /* this is a lie of course */ +} + + +/* + * Output a buffer to a file, converting from netascii if requested. + * CR,NUL -> CR and CR,LF => LF. + * Note spec is undefined if we get CR as last byte of file or a + * CR followed by anything else. In this case we leave it alone. + */ +int +write_behind(FILE *file, int convert) +{ + char *buf; + int count; + register int ct; + register char *p; + register int c; /* current character */ + struct bf *b; + struct kdumphdr *dp; + + b = &bfs[nextone]; + if (b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ + + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = (struct kdumphdr *)b->buf; + nextone = !nextone; /* incr for next time */ + buf = dp->th_data; + + if (count <= 0) return -1; /* nak logic? */ + + if (convert == 0) + return write(fileno(file), buf, count); + + p = buf; + ct = count; + while (ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if (prevchar == '\r') { /* if prev char was cr */ + if (c == '\n') /* if have cr,lf then just */ + fseek(file, -1, 1); /* smash lf on top of the cr */ + else + if (c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + putc(c, file); +skipit: + prevchar = c; + } + return count; +} + + +/* When an error has occurred, it is possible that the two sides + * are out of synch. Ie: that what I think is the other side's + * response to packet N is really their response to packet N-1. + * + * So, to try to prevent that, we flush all the input queued up + * for us on the network connection on our host. + * + * We return the number of packets we flushed (mostly for reporting + * when trace is active). + */ + +/*int f;socket to flush */ +int +synchnet(int f) +{ + int i, j = 0; + char rbuf[kdp_crashdump_pkt_size]; + struct sockaddr_in from; + socklen_t fromlen; + + while (1) { + (void) ioctl(f, FIONREAD, &i); + if (i) { + j++; + fromlen = sizeof from; + (void) recvfrom(f, rbuf, sizeof (rbuf), 0, + (struct sockaddr *)&from, &fromlen); + } else { + return(j); + } + } +} diff --git a/network_cmds/kdumpd.tproj/kdumpsubs.h b/network_cmds/kdumpd.tproj/kdumpsubs.h new file mode 100644 index 0000000..b7ec1c6 --- /dev/null +++ b/network_cmds/kdumpd.tproj/kdumpsubs.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kdumpsubs.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Prototypes for read-ahead/write-behind subroutines for kdump user and + * server. + */ +struct kdumphdr *r_init __P((void)); +void read_ahead __P((FILE *, int)); +int readit __P((FILE *, struct kdumphdr **, int)); + +int synchnet __P((int)); + +struct kdumphdr *w_init __P((void)); +int write_behind __P((FILE *, int)); +int writeit __P((FILE *, struct kdumphdr **, int, int)); + diff --git a/network_cmds/mnc.tproj/LICENCE b/network_cmds/mnc.tproj/LICENCE new file mode 100644 index 0000000..4fec3e3 --- /dev/null +++ b/network_cmds/mnc.tproj/LICENCE @@ -0,0 +1,37 @@ +/* + * Colm MacCarthaigh, <colm@apache.org> + * + * Copyright (c) 2007, Colm MacCarthaigh. + * Copyright (c) 2004 - 2006, HEAnet Ltd. + * + * This software is an open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the HEAnet Ltd. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + diff --git a/network_cmds/mnc.tproj/README b/network_cmds/mnc.tproj/README new file mode 100644 index 0000000..77cbaa8 --- /dev/null +++ b/network_cmds/mnc.tproj/README @@ -0,0 +1,63 @@ +1 MNC - Multicast NetCat + ------------------------ + +1. Introduction + +mnc is a simple, one-direction-at-a-time, "netcat"-like application using +multicast. The aim is to provide a tool for easy debugging and testing when +setting up a multicast network or host. MNC supports IPv4 and IPv6 +any-source-multicast and single-source-multicast, but depending on your +platform some of those features may not be available: + + L = Listen (Implies IGMP/MLD mupport) + S = Send + + +----------+----------+----------+----------+----------+ + | Platform | IPv4 ASM | IPv4 SSM | IPv6 ASM | IPv6 SSM | + +----------+----------+----------+----------+----------+ + | *nix | L + S | L + S | L + S | L + S | + +----------+----------+----------+----------+----------+ + | Win2k | L + S | S | None | None | + +----------+----------+----------+----------+----------+ + | WinXP | L + S | L + S | S | S | + +----------+----------+----------+----------+----------+ + | Win2k3 | L + S | L + S | S | S | + +----------+----------+----------+----------+----------+ + +man doc/mnc.1 for information and help on how to run multicast. + +2. Supported platforms + +As of September 2004, mnc has been compiled and tested with Linux 2.6.8 +kernels, the BSD KAME stack and Windows XP Profesional. Currently automatic +interface selection does not work on a KAME-based host and you will need to +specify the interface when in listening mode. + +3. Installing mnc from source on UNIX: + + ./configure ; make ; make install + +4. Compiling from source on Windows: + +nmc is compilable with free utilities available from Microsoft. You need to +download and install the free (as in beer) MS Visual C++ command-line tools +from: + + http://msdn.microsoft.com/visualc/vctoolkit2003/ + +and the SDK relevant to your platform from: + + http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ + +You can run a Visual C++ command-line session, and use: + + cl /DWINDOWS=1 /TC /c *.c + link /fixed /out:mnc.exe *.obj ws2_32.lib + +to compile a working mnc.exe. The Windows version of MNC does not yet fully +support IPv6 due to underlying limitation in the Operating System. + +5. Reporting problems + +Any problems, bugs or suggested features should be mailed to colm +at apache.org. diff --git a/network_cmds/mnc.tproj/mnc.1 b/network_cmds/mnc.tproj/mnc.1 new file mode 100644 index 0000000..ed1073b --- /dev/null +++ b/network_cmds/mnc.tproj/mnc.1 @@ -0,0 +1,101 @@ +.\" +.\" mnc.1 -- mnc manual +.\" +.\" Colm MacCárthaigh, <colm@apache.org> +.\" +.\" Copyright (c) 2007 Coolm MacCarthaigh. +.\" Copyright (c) 2004-2006 HEAnet Ltd. +.\" +.\" This software is an open source. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" Redistributions of source code must retain the above copyright notice, +.\" this list of conditions and the following disclaimer. +.\" +.\" Redistributions in binary form must reproduce the above copyright notice, +.\" this list of conditions and the following disclaimer in the documentation +.\" and/or other materials provided with the distribution. +.\" +.\" Neither the name of the HEAnet Ltd. nor the names of its contributors may +.\" be used to endorse or promote products derived from this software without +.\" specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.TH mnc 1 "17 September 2004" "mnc" +.SH NAME +mnc \- Multicast NetCat +.SH SYNOPSIS +.BR mnc +[ -l ] [ -i interface ] [ -p port ] group-id [ source-address ] +.SH DESCRIPTION +.B mnc +is designed for simple multicast debugging and testing. It supports +IPv4 and IPv6 any-source multicast and source-specific multicast. +In standard mode it will multicast the standard-input verbatim. +In listening mode it recieves the content and displays it on the +standard output. +.PP +When given the optional source-address, mnc will attempt to use +source-specific multicast. You may also specify this address +in standard mode and mnc will use this source-address for +outgoing packets. +.PP +.SH OPTIONS +.IP \-l +Listen for multicast packets. Default is to send. +.IP \-i\ "interface" +When listening for multicast packets, use the specified interface. +.IP \-p\ "port" +Specify a UDP port to use for sending or receiving packets. Default is to use port 1234. +.SH "SEE ALSO" +.BR nc (1) +.PP +.SH EXAMPLES +To send IPv4 packets to an ASM group-id: +.PP +.RS +mnc 233.1.2.3 +.RE +.PP +To receive IPv4 ASM packets from the same group-id, using eth0 as +the listening interface: +.PP +.RS +mnc -l -i eth0 233.1.2.3 +.RE +.PP +To receive IPv4 packets from an SSM group-id, using eth1 as the +listening interface and 193.1.219.90 as the permitted source: +.PP +.RS +mnc -l -i eth1 232.0.0.1 193.1.219.90 +.RE +.PP +To receive IPv6 packets from an SSM group-id, using eth1 as the +listening interface and 2001:770:18:2::90 as the permitted source: +.PP +.RS +mnc -l -i eth1 ff31::12 2001:770:18:2::90 +.RE +.PP +.SH CREDITS +mnc is by Colm MacCárthaigh <colm@apache.org> and is available from: +.PP +http://people.apache.org/~colm/mnc/ +.PP +Additional multicast development and support provided by John Lyons +<john.lyons@heanet.ie> and Eoin Kenny <eoin.kenny@heanet.ie> diff --git a/network_cmds/mnc.tproj/mnc.h b/network_cmds/mnc.tproj/mnc.h new file mode 100644 index 0000000..db6e68e --- /dev/null +++ b/network_cmds/mnc.tproj/mnc.h @@ -0,0 +1,92 @@ +/* + * $Id: mnc.h,v 1.4 2004/09/22 14:07:10 colmmacc Exp $ + * + * mnc.h -- Multicast NetCat + * + * Colm MacCarthaigh, <colm@apache.org> + * + * Copyright (c) 2007, Colm MacCarthaigh. + * Copyright (c) 2004 - 2006, HEAnet Ltd. + * + * This software is an open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the HEAnet Ltd. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _MNC_H_ +#define _MNC_H_ + +#ifndef WINDOWS + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#else + +#include <winsock2.h> +#include <ws2tcpip.h> + +#endif + +/* The UDP port MNC will use by default */ +#define MNC_DEFAULT_PORT "1234" + +struct mnc_configuration +{ + /* Are we sending or recieving ? */ + enum {SENDER, LISTENER} mode; + + /* What UDP port are we using ? */ + char * port; + + /* The group-id */ + struct addrinfo * group; + + /* The source */ + struct addrinfo * source; + + /* An interface index for listening */ + char * iface; +}; + + +/* Functions in mnc_opts.c */ +void usage(void); +struct mnc_configuration * parse_arguments(int argc, char **argv); + +/* Functions in mnc_multicast.c */ +int multicast_setup_listen(int, struct addrinfo *, struct addrinfo *, char *); +int multicast_setup_send(int, struct addrinfo *, struct addrinfo *); + +/* Functions in mnc_error.c */ +void mnc_warning(char * string, ...); +void mnc_error(char * string, ...); + +#endif /* _MNC_H_ */ diff --git a/network_cmds/mnc.tproj/mnc_error.c b/network_cmds/mnc.tproj/mnc_error.c new file mode 100644 index 0000000..4478e7e --- /dev/null +++ b/network_cmds/mnc.tproj/mnc_error.c @@ -0,0 +1,95 @@ +/* + * $Id: mnc_error.c,v 1.2 2004/09/22 16:02:26 colmmacc Exp $ + * + * mnc_multicast.c -- Multicast NetCat + * + * Colm MacCarthaigh, <colm@apache.org> + * + * Copyright (c) 2007, Colm MacCarthaigh. + * Copyright (c) 2004 - 2006, HEAnet Ltd. + * + * This software is an open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the HEAnet Ltd. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "mnc.h" + +void mnc_warning(char * string, ...) +{ + va_list ap; + + /* Do the vararg stuff */ + va_start(ap, string); + + /* Output our name */ + if (fprintf(stderr, "mnc: ") < 0) + { + exit(2); + } + + /* Output our error */ + if (vfprintf(stderr, string, ap) < 0) + { + exit(2); + } + + /* End the vararg stuff */ + va_end(ap); +} + +void mnc_error(char * string, ...) +{ + va_list ap; + + /* Do the vararg stuff */ + va_start(ap, string); + + /* Output our name */ + if (fprintf(stderr, "mnc: ") < 0) + { + exit(2); + } + + /* Output our error */ + if (vfprintf(stderr, string, ap) < 0) + { + exit(2); + } + + /* End the vararg stuff */ + va_end(ap); + + /* Die! */ + exit(1); +} diff --git a/network_cmds/mnc.tproj/mnc_main.c b/network_cmds/mnc.tproj/mnc_main.c new file mode 100644 index 0000000..1e5c5af --- /dev/null +++ b/network_cmds/mnc.tproj/mnc_main.c @@ -0,0 +1,131 @@ +/* + * $Id: mnc_main.c,v 1.12 2004/09/22 19:14:23 colmmacc Exp $ + * + * mnc_main.c -- Multicast NetCat + * + * Colm MacCarthaigh, <colm@apache.org> + * + * Copyright (c) 2007, Colm MacCarthaigh. + * Copyright (c) 2004 - 2006, HEAnet Ltd. + * + * This software is an open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the HEAnet Ltd. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WINDOWS + +/* Non-windows includes */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <stdio.h> + +#else + +/* Windows-specific includes */ + +#include <winsock2.h> +#include <ws2tcpip.h> +#include <stdlib.h> +#include <stdio.h> + +#endif /* WINDOWS */ + +#include "mnc.h" + +int main(int argc, char **argv) +{ + /* Utility variables */ + int sock, + len; + char buffer[1024]; + + /* Our main configuration */ + struct mnc_configuration * config; + +#ifdef WINDOWS + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) + { + mnc_error("This operating system is not supported\n"); + } +#endif + + /* Parse the command line */ + config = parse_arguments(argc, argv); + + /* Create a socket */ + if ((sock = socket(config->group->ai_family, config->group->ai_socktype, + config->group->ai_protocol)) < 0) + { + mnc_error("Could not create socket\n"); + } + + /* Are we supposed to listen? */ + if (config->mode == LISTENER) + { + /* Set up the socket for listening */ + if (multicast_setup_listen(sock, config->group, config->source, + config->iface) < 0) + { + mnc_error("Can not listen for multicast packets.\n"); + } + + /* Recieve the packets */ + while ((len = recvfrom(sock, buffer, sizeof(buffer), + 0, NULL, NULL)) >= 0) + { + write(STDOUT_FILENO, buffer, len); + } + } + else /* Assume MODE == SENDER */ + { + /* Set up the socket for sending */ + if (multicast_setup_send(sock, config->group, config->source) + < 0) + { + mnc_error("Can not send multicast packets\n"); + } + + /* Send the packets */ + while((len = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) + { + sendto(sock, buffer, len, 0, config->group->ai_addr, + config->group->ai_addrlen); + } + } + + /* Close the socket */ + close(sock); + + return 0; +} diff --git a/network_cmds/mnc.tproj/mnc_multicast.c b/network_cmds/mnc.tproj/mnc_multicast.c new file mode 100644 index 0000000..35f3415 --- /dev/null +++ b/network_cmds/mnc.tproj/mnc_multicast.c @@ -0,0 +1,404 @@ +/* + * $Id: mnc_multicast.c,v 1.8 2004/09/22 19:14:23 colmmacc Exp $ + * + * mnc_multicast.c -- Multicast NetCat + * + * Colm MacCarthaigh, <colm@apache.org> + * + * copyright (c) 2007, Colm MacCarthaigh. + * Copyright (c) 2004 - 2006, HEAnet Ltd. + * + * This software is an open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the HEAnet Ltd. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WINDOWS + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <string.h> +#include <netdb.h> +#include <errno.h> + +#else + +#include <sys/types.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#include <stdlib.h> + +#endif + +#include "mnc.h" + +#ifndef MCAST_JOIN_GROUP + +#ifdef IP_ADD_SOURCE_MEMBERSHIP +int mnc_join_ipv4_ssm(int socket, struct addrinfo * group, + struct addrinfo * source, char * iface) +{ + struct ip_mreq_source multicast_request; + + if (iface != NULL) + { + /* See if interface is a literal IPv4 address */ + if ((multicast_request.imr_interface.s_addr = + inet_addr(iface)) == INADDR_NONE) + { + mnc_warning("Invalid interface address\n"); + return -1; + } + } + else + { + /* set the interface to the default */ + multicast_request.imr_interface.s_addr = htonl(INADDR_ANY); + } + + multicast_request.imr_multiaddr.s_addr = + ((struct sockaddr_in *)group->ai_addr)->sin_addr.s_addr; + + multicast_request.imr_sourceaddr.s_addr = + ((struct sockaddr_in *)source->ai_addr)->sin_addr.s_addr; + + /* Set the socket option */ + if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, + (char *) &multicast_request, + sizeof(multicast_request)) != 0) + { + mnc_warning("Could not join the multicast group: %s\n", + strerror(errno)); + + return -1; + } + + return 0; +} +#else + +int mnc_join_ipv4_ssm(int socket, struct addrinfo * group, + struct addrinfo * source, char * iface) +{ + mnc_warning("Sorry, No support for IPv4 source-specific multicast in this build\n"); + + return -1; +} +#endif + +int mnc_join_ipv6_ssm(int socket, struct addrinfo * group, + struct addrinfo * source, char * iface) +{ + mnc_warning("Sorry, No support for IPv6 source-specific multicast in this build\n"); + + return -1; +} +#else /* if MCAST_JOIN_GROUP .. */ + +#define mnc_join_ipv6_asm(a, b, c) mnc_join_ip_asm((a), (b), (c)) +#define mnc_join_ipv4_asm(a, b, c) mnc_join_ip_asm((a), (b), (c)) + +int mnc_join_ip_asm(int socket, struct addrinfo * group, char * iface) +{ + struct group_req multicast_request; + int ip_proto; + + if (group->ai_family == AF_INET6) + { + ip_proto = IPPROTO_IPV6; + } + else + { + ip_proto = IPPROTO_IP; + } + + if (iface != NULL) + { + if ((multicast_request.gr_interface = if_nametoindex(iface)) + == 0) + { + mnc_warning("Ignoring unknown interface: %s\n", iface); + } + } + else + { + multicast_request.gr_interface = 0; + } + + memcpy(&multicast_request.gr_group, group->ai_addr, group->ai_addrlen); + + /* Set the socket option */ + if (setsockopt(socket, ip_proto, MCAST_JOIN_GROUP, (char *) + &multicast_request, sizeof(multicast_request)) != 0) + { + mnc_warning("Could not join the multicast group: %s\n", + strerror(errno)); + + return -1; + } + + return 0; +} + +#endif /* MCAST_JOIN_GROUP */ + +#ifndef MCAST_JOIN_SOURCE_GROUP +int mnc_join_ipv4_asm(int socket, struct addrinfo * group, char * iface) +{ + struct ip_mreq multicast_request; + + if (iface != NULL) + { + /* See if interface is a literal IPv4 address */ + if ((multicast_request.imr_interface.s_addr = + inet_addr(iface)) == INADDR_NONE) + { + mnc_warning("Invalid interface address\n"); + return -1; + } + } + else + { + /* Set the interface to the default */ + multicast_request.imr_interface.s_addr = htonl(INADDR_ANY); + } + + multicast_request.imr_multiaddr.s_addr = + ((struct sockaddr_in *)group->ai_addr)->sin_addr.s_addr; + + /* Set the socket option */ + if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *) &multicast_request, + sizeof(multicast_request)) != 0) + { + mnc_warning("Could not join the multicast group: %s\n", + strerror(errno)); + + return -1; + } + + return 0; +} + +int mnc_join_ipv6_asm(int socket, struct addrinfo * group, char * iface) +{ + mnc_warning("Sorry, No support for IPv6 any-source multicast in this build\n"); + + return -1; +} +#else /* if MCAST_JOIN_SOURCE_GROUP ... */ + +#define mnc_join_ipv4_ssm(a, b, c, d) mnc_join_ip_ssm((a), (b), (c), (d)) +#define mnc_join_ipv6_ssm(a, b, c, d) mnc_join_ip_ssm((a), (b), (c), (d)) + +int mnc_join_ip_ssm(int socket, struct addrinfo * group, + struct addrinfo * source, + char * iface) +{ + struct group_source_req multicast_request; + int ip_proto; + + if (group->ai_family == AF_INET6) + { + ip_proto = IPPROTO_IPV6; + } + else + { + ip_proto = IPPROTO_IP; + } + + if (iface != NULL) + { + if ((multicast_request.gsr_interface = if_nametoindex(iface)) + == 0) + { + mnc_warning("Ignoring unknown interface: %s\n", iface); + } + } + else + { + multicast_request.gsr_interface = 0; + } + + memcpy(&multicast_request.gsr_group, group->ai_addr, group->ai_addrlen); + memcpy(&multicast_request.gsr_source, source->ai_addr, + source->ai_addrlen); + + /* Set the socket option */ + if (setsockopt(socket, ip_proto, MCAST_JOIN_SOURCE_GROUP, + (char *) &multicast_request, + sizeof(multicast_request)) != 0) + { + mnc_warning("Could not join the multicast group: %s\n", + strerror(errno)); + + return -1; + } + + return 0; +} +#endif /* MCAST_JOIN_SOURCE_GROUP */ + +int multicast_setup_listen(int socket, struct addrinfo * group, + struct addrinfo * source, char * iface) +{ + size_t rcvbuf; + +#ifndef WINDOWS + /* bind to the group address before anything */ + if (bind(socket, group->ai_addr, group->ai_addrlen) != 0) + { + mnc_warning("Could not bind to group-id\n"); + return -1; + } +#else + if (group->ai_family == AF_INET) + { + struct sockaddr_in sin; + + sin.sin_family = group->ai_family; + sin.sin_port = group->ai_port; + sin.sin_addr = INADDR_ANY; + + if (bind(socket, (struct sockaddr *) sin, + sizeof(sin)) != 0) + { + mnc_warning("Could not bind to ::\n"); + return -1; + } + } + else if (group->ai_family == AF_INET6) + { + struct sockaddr_in6 sin6; + + sin6.sin6_family = group->ai_family; + sin6.sin6_port = group->ai_port; + sin6.sin6_addr = in6addr_any; + + if (bind(socket, (struct sockaddr *) sin6, + sizeof(sin6)) != 0) + { + mnc_warning("Could not bind to ::\n"); + return -1; + } + } +#endif + + /* Set a receive buffer size of 64k */ + rcvbuf = 1 << 15; + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &rcvbuf, + sizeof(rcvbuf)) < 0) { + mnc_warning("Could not set receive buffer to 64k\n"); + } + + if (source != NULL) + { + if (group->ai_family == AF_INET6) + { + /* Use whatever IPv6 API is appropriate */ + return + mnc_join_ipv6_ssm(socket, group, source, iface); + } + else if (group->ai_family == AF_INET) + { + /* Use the fully portable IPv4 API */ + return + mnc_join_ipv4_ssm(socket, group, source, iface); + } + else + { + mnc_warning("Only IPv4 and IPv6 are supported\n"); + return -1; + } + } + else + { + if (group->ai_family == AF_INET6) + { + /* Use the fully portable IPv4 API */ + return + mnc_join_ipv6_asm(socket, group, iface); + } + else if (group->ai_family == AF_INET) + { + /* Use the fully portable IPv4 API */ + return + mnc_join_ipv4_asm(socket, group, iface); + } + else + { + mnc_warning("Only IPv4 and IPv6 are supported\n"); + return -1; + } + } + + /* We should never get here */ + return -1; +} + + +int multicast_setup_send(int socket, struct addrinfo * group, + struct addrinfo * source) +{ + int ttl = 255; + + if (source != NULL) + { + /* bind to the address before anything */ + if (bind(socket, source->ai_addr, source->ai_addrlen) != 0) + { + mnc_warning("Could not bind to source-address\n"); + return -1; + } + } + + if (group->ai_family == AF_INET) + { + if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *) + &ttl, sizeof(ttl)) != 0) + { + mnc_warning("Could not increase the TTL\n"); + return -1; + } + } + else if (group->ai_family == AF_INET6) + { + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (char *) &ttl, sizeof(ttl)) != 0) + { + mnc_warning("Could not increase the hop-count\n"); + return -1; + } + } + + return 0; +} diff --git a/network_cmds/mnc.tproj/mnc_opts.c b/network_cmds/mnc.tproj/mnc_opts.c new file mode 100644 index 0000000..606746f --- /dev/null +++ b/network_cmds/mnc.tproj/mnc_opts.c @@ -0,0 +1,182 @@ +/* + * $Id: mnc_opts.c,v 1.3 2004/09/22 16:02:26 colmmacc Exp $ + * + * mnc_opts.c -- Multicast NetCat + * + * Colm MacCarthaigh, <colm@apache.org> + * + * Copyright (c) 2007, Colm MacCarthaigh. + * Copyright (c) 2004 - 2006, HEAnet Ltd. + * + * This software is an open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the HEAnet Ltd. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifndef WINDOWS + +/* UNIX-y includes */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#else + +/* WINDOWS-y includes */ +#include <winsock2.h> +#include <ws2tcpip.h> +#endif + +#include "mnc.h" + +/* Display a usage statement */ +void usage(void) +{ + fprintf(stderr, + "Usage: mnc [-l] [-i interface] [-p port] group-id " + "[source-address]\n\n" + "-l : listen mode\n" + "-i : specify interface to listen\n" + "-p : specify port to listen/send on\n\n"); + exit(1); +} + +struct mnc_configuration * parse_arguments(int argc, char **argv) +{ + /* Utility variables */ + int optind, + errorcode; + struct addrinfo hints; + + /* Our persisting configuration */ + static struct mnc_configuration config; + + /* Set some defaults */ + config.mode = SENDER; + config.port = MNC_DEFAULT_PORT; + config.iface = NULL; + config.source = NULL; + + /* Loop through the arguments */ + for (optind = 1; optind < (argc - 1); optind++) + { + if ( (argv[optind][0] == '-') || (argv[optind][0] == '/') ) + { + switch(argv[optind][1]) + { + /* Set listening mode */ + case 'l': config.mode = LISTENER; + break; + + /* Set port */ + case 'p': config.port = argv[++optind]; + break; + + /* Set an interface */ + case 'i': config.iface = argv[++optind]; + break; + + /* Unrecognised option */ + default: usage(); + break; + } + } + else + { + /* assume we've ran out of options */ + break; + } + } + + /* There's a chance we were passed one option */ + if (optind >= argc || argv[optind][0] == '-') + { + usage(); + } + + /* Now make sure we have either exactly 1 or 2 more arguments */ + if ( (argc - optind) != 1 && (argc - optind) != 2 ) + { + /* We have not been given the right ammount of + arguments */ + usage(); + } + + /* You can't have an interface without also listening */ + if (config.mode == SENDER && config.iface != NULL) + { + mnc_error("You may only specify the interface when in" + " listening mode\n"); + } + + /* Set some hints for getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + + /* We want a UDP socket */ + hints.ai_socktype = SOCK_DGRAM; + + /* Don't do any name-lookups */ + hints.ai_flags = AI_NUMERICHOST; + + /* Get the group-id information */ + if ( (errorcode = + getaddrinfo(argv[optind], config.port, &hints, &config.group)) != 0) + { + mnc_error("Error getting group-id address information: %s\n", + gai_strerror(errorcode)); + } + + /* Move on to next argument */ + optind++; + + /* Get the source information */ + if ( (argc - optind) == 1) + { + + if ( (errorcode = + getaddrinfo(argv[optind], config.port, &hints, &config.source)) + != 0) + { + mnc_error("Error getting source-address information: %s\n", + gai_strerror(errorcode)); + } + + /* Confirm that the source and group are in the same Address Family */ + if ( config.source->ai_family != config.group->ai_family ) + { + mnc_error("Group ID and Source address are not of " + "the same type\n"); + } + } + + return &config; +} diff --git a/network_cmds/mptcp_client/conn_lib.c b/network_cmds/mptcp_client/conn_lib.c new file mode 100644 index 0000000..1c83915 --- /dev/null +++ b/network_cmds/mptcp_client/conn_lib.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2012-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// +// Created by Anumita Biswas on 10/30/12. +// + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <errno.h> + + +#include "conn_lib.h" + +int +copyassocids(int s, sae_associd_t **aidpp, uint32_t *cnt) +{ + struct so_aidreq aidr; + sae_associd_t *buf; + int err; + + if (aidpp == NULL || cnt == NULL) { + errno = EINVAL; + return (-1); + } + *aidpp = NULL; + *cnt = 0; + + bzero(&aidr, sizeof (aidr)); + + err = ioctl(s, SIOCGASSOCIDS, &aidr); + if (err != 0) + return (-1); + + /* none, just return */ + if (aidr.sar_cnt == 0) + return (0); + + buf = calloc(aidr.sar_cnt, sizeof (sae_associd_t)); + if (buf == NULL) + return (-1); + + aidr.sar_aidp = buf; + err = ioctl(s, SIOCGASSOCIDS, &aidr); + if (err != 0) { + free(buf); + return (-1); + } + + *aidpp = buf; + *cnt = aidr.sar_cnt; + + return (0); +} + +void +freeassocids(sae_associd_t *aidp) +{ + free(aidp); +} + +int +copyconnids(int s, sae_associd_t aid, sae_connid_t **cidp, uint32_t *cnt) +{ + struct so_cidreq cidr; + sae_connid_t *buf; + int err; + + if (cidp == NULL || cnt == NULL) { + errno = EINVAL; + return (-1); + } + *cidp = NULL; + *cnt = 0; + + bzero(&cidr, sizeof (cidr)); + + cidr.scr_aid = aid; + err = ioctl(s, SIOCGCONNIDS, &cidr); + if (err != 0) + return (-1); + + /* none, just return */ + if (cidr.scr_cnt == 0) + return (0); + + buf = calloc(cidr.scr_cnt, sizeof (sae_connid_t)); + if (buf == NULL) + return (-1); + + cidr.scr_cidp = buf; + err = ioctl(s, SIOCGCONNIDS, &cidr); + if (err != 0) { + free(buf); + return (-1); + } + + *cidp = buf; + *cnt = cidr.scr_cnt; + + return (0); +} + +void +freeconnids(sae_connid_t *cidp) +{ + free(cidp); +} + +int +copyconninfo(int s, sae_connid_t cid, conninfo_t **cfop) +{ + struct sockaddr *src = NULL, *dst = NULL, *aux = NULL; + struct so_cinforeq scir; + conninfo_t *buf = NULL; + + if (cfop == NULL) { + errno = EINVAL; + goto error; + } + *cfop = NULL; + + bzero(&scir, sizeof (scir)); + + scir.scir_cid = cid; + if (ioctl(s, SIOCGCONNINFO, &scir) != 0) + goto error; + + if (scir.scir_src_len != 0) { + src = calloc(1, scir.scir_src_len); + if (src == NULL) + goto error; + scir.scir_src = src; + } + if (scir.scir_dst_len != 0) { + dst = calloc(1, scir.scir_dst_len); + if (dst == NULL) + goto error; + scir.scir_dst = dst; + } + if (scir.scir_aux_len != 0) { + aux = calloc(1, scir.scir_aux_len); + if (aux == NULL) + goto error; + scir.scir_aux_data = aux; + } + + if (ioctl(s, SIOCGCONNINFO, &scir) != 0) + goto error; + + buf = calloc(1, sizeof (*buf)); + if (buf == NULL) + goto error; + + // When we query for the length using the first ioctl call above, the kernel + // tells us the length of the aux structure so we know how much to allocate + // memory. There may not be any aux data, which will be indicated by the aux + // data length using the second ioctl call. + if (scir.scir_aux_len == 0 && aux != NULL) { + free(aux); + aux = NULL; + scir.scir_aux_data = NULL; + } + + buf->ci_flags = scir.scir_flags; + buf->ci_ifindex = scir.scir_ifindex; + buf->ci_src = src; + buf->ci_dst = dst; + buf->ci_error = scir.scir_error; + buf->ci_aux_type = scir.scir_aux_type; + buf->ci_aux_data = aux; + *cfop = (conninfo_t*)buf; + + return (0); + +error: + if (src != NULL) + free(src); + if (dst != NULL) + free(dst); + if (aux != NULL) + free(aux); + if (buf != NULL) + free(buf); + + return (-1); +} + +void +freeconninfo(conninfo_t *cfo) +{ + if (cfo->ci_src != NULL) + free(cfo->ci_src); + + if (cfo->ci_dst != NULL) + free(cfo->ci_dst); + + if (cfo->ci_aux_data != NULL) + free(cfo->ci_aux_data); + + free(cfo); +} diff --git a/network_cmds/mptcp_client/conn_lib.h b/network_cmds/mptcp_client/conn_lib.h new file mode 100644 index 0000000..b69b73c --- /dev/null +++ b/network_cmds/mptcp_client/conn_lib.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// +// Created by Anumita Biswas on 10/30/12. +// + +#ifndef mptcp_client_conn_lib_h +#define mptcp_client_conn_lib_h + +typedef struct conninfo { + __uint32_t ci_flags; /* see flags in sys/socket.h (CIF_CONNECTING, etc...) */ + __uint32_t ci_ifindex; /* outbound interface */ + struct sockaddr *ci_src; /* source address */ + struct sockaddr *ci_dst; /* destination address */ + int ci_error; /* saved error */ + __uint32_t ci_aux_type; /* auxiliary data type */ + void *ci_aux_data; /* auxiliary data */ +} conninfo_t; + +extern int copyassocids(int, sae_associd_t **, uint32_t *); +extern void freeassocids(sae_associd_t *); +extern int copyconnids(int, sae_associd_t, sae_connid_t **, uint32_t *); +extern void freeconnids(sae_connid_t *); +extern int copyconninfo(int, sae_connid_t, conninfo_t **); +extern void freeconninfo(conninfo_t *); + +#endif diff --git a/network_cmds/mptcp_client/mptcp_client.1 b/network_cmds/mptcp_client/mptcp_client.1 new file mode 100644 index 0000000..2ca3cad --- /dev/null +++ b/network_cmds/mptcp_client/mptcp_client.1 @@ -0,0 +1,21 @@ +.Dd 6/12/14
+.Dt mptcp_client 1
+.Os Darwin
+.Sh NAME
+.Nm mptcp_client
+.Nd Tool to exercise MPTCP.
+.Sh SYNOPSIS
+.Nm
+.Ar hostname
+.Ar port
+.Ar reqlen
+.Ar rsplen
+.Ar times
+.Ar alt_addr
+.Ar 0
+.Ar longlived
+.Sh DESCRIPTION
+.Nm
+is as an MPTCP client test tool that use the
+.Xr socket 2
+API.
diff --git a/network_cmds/mptcp_client/mptcp_client.c b/network_cmds/mptcp_client/mptcp_client.c new file mode 100644 index 0000000..770aabc --- /dev/null +++ b/network_cmds/mptcp_client/mptcp_client.c @@ -0,0 +1,701 @@ +/* + * Copyright (c) 2012-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +// +// Created by Anumita Biswas on 7/17/12. +// + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netdb.h> +#include <errno.h> +#include <arpa/inet.h> +#include <err.h> +#include <sysexits.h> +#include <getopt.h> + +#include "conn_lib.h" + +struct so_cordreq socorder; +static void showmpinfo(int s); + +#define MSG_HDR "Message Header" +#define RESPONSE "I got your message" + +static int verbose = 0; + +static int32_t thiszone = 0; /* time difference with gmt */ + +char *setup_buffer1(int bufsz) +{ + int i = 0, j = 1; + char *buf; + + buf = malloc(bufsz); + if (!buf) + return NULL; + + bzero(buf, bufsz); + strlcpy(buf, MSG_HDR, bufsz); + + for (i = sizeof(MSG_HDR); i < bufsz; i++) { + buf[i] = j; + j++; + if (j >= 255) + j = 1; + } + return buf; +} + +char *setup_buffer2(int bufsz) +{ + int i = 0; + char j = 'A'; + char *buf; + + buf = malloc(bufsz); + if (!buf) + return NULL; + + bzero(buf, bufsz); + strlcpy(buf, MSG_HDR, bufsz); + + for (i = sizeof(MSG_HDR); i < bufsz; i++) { + buf[i] = j; + j++; + if (j >= 'z') + j = 'A'; + } + return buf; +} + +char *setup_buffer3(int bufsz) +{ + char *buf; + + buf = malloc(bufsz); + if (!buf) + return NULL; + + bzero(buf, bufsz); + return buf; +} + +/* + * Returns the difference between gmt and local time in seconds. + * Use gmtime() and localtime() to keep things simple. + * from tcpdump/gmt2local.c + */ +static int32_t +gmt2local(time_t t) +{ + int dt, dir; + struct tm *gmt, *loc; + struct tm sgmt; + + if (t == 0) + t = time(NULL); + gmt = &sgmt; + *gmt = *gmtime(&t); + loc = localtime(&t); + dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + + (loc->tm_min - gmt->tm_min) * 60; + + /* + * If the year or julian day is different, we span 00:00 GMT + * and must add or subtract a day. Check the year first to + * avoid problems when the julian day wraps. + */ + dir = loc->tm_year - gmt->tm_year; + if (dir == 0) + dir = loc->tm_yday - gmt->tm_yday; + dt += dir * 24 * 60 * 60; + + return (dt); +} + +/* + * Print the timestamp + * from tcpdump/util.c + */ +static void +ts_print(void) +{ + int s; + struct timeval tv; + + gettimeofday(&tv, NULL); + + /* Default */ + s = (tv.tv_sec + thiszone) % 86400; + printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, + (u_int32_t)tv.tv_usec); +} + +static const char * +basename(const char * str) +{ + const char *last_slash = strrchr(str, '/'); + + if (last_slash == NULL) + return (str); + else + return (last_slash + 1); +} + +struct option_desc { + const char *option; + const char *description; + int required; +}; + +struct option_desc option_desc_list[] = { + { "--host addr", "address of server to connect to", 1 }, + { "--port n", "port of server to connect to", 1 }, + { "--reqlen n", "length of request (256 by default)", 0 }, + { "--rsplen n", "length of response (256 by default)", 0 }, + { "--ntimes n", "number of time to send request (1 by default)", 0 }, + { "--alt_addr addr", "alternate server to connect to", 0 }, + { "--verbose", "increase verbosity", 0 }, + { "--help", "display this help", 0 }, + + { NULL, NULL, 0 } /* Mark end of list */ +}; + +static void +usage(const char *cmd) +{ + struct option_desc *option_desc; + char *usage_str = malloc(LINE_MAX); + size_t usage_len; + + if (usage_str == NULL) + err(1, "%s: malloc(%d)", __func__, LINE_MAX); + + usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd)); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + int len; + + if (option_desc->required) + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option); + else + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option); + if (len < 0) + err(1, "%s: snprintf(", __func__); + + usage_len += len; + if (usage_len > LINE_MAX) + break; + } + printf("%s\n", usage_str); + printf("options:\n"); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + printf(" %-24s # %s\n", option_desc->option, option_desc->description); + } + printf("\n"); + printf("# legacy usage: "); +} + +static struct option longopts[] = { + { "host", required_argument, NULL, 'c' }, + { "port", required_argument, NULL, 'p' }, + { "reqlen", required_argument, NULL, 'r' }, + { "rsplen", required_argument, NULL, 'R' }, + { "ntimes", required_argument, NULL, 'n' }, + { "alt_addr", required_argument, NULL, 'a' }, + { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'v' }, + { "quiet", no_argument, NULL, 'q' }, + { NULL, 0, NULL, 0 } +}; + +static int +sprint_sockaddr(char *str, socklen_t strlen, struct sockaddr *sa) +{ + int retval = 0; + + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in*)sa; + char str4[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &sin->sin_addr, str4, sizeof(str4)); + + retval = snprintf(str, strlen, "%s:%u", str4, ntohs(sin->sin_port)); + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; + char str6[INET6_ADDRSTRLEN]; + char ifname[IF_NAMESIZE]; + char scopestr[2 + IF_NAMESIZE]; + + inet_ntop(AF_INET6, &sin6->sin6_addr, str6, sizeof(str6)); + + if (sin6->sin6_scope_id == 0) + *scopestr = '\0'; + else { + if_indextoname(sin6->sin6_scope_id, ifname); + snprintf(scopestr, sizeof(scopestr), "%%%s", ifname); + } + + retval = snprintf(str, strlen, "%s%s:%u", + str6, + scopestr, + ntohs(sin6->sin6_port)); + } + return (retval); +} + +int main(int argc, char * const *argv) +{ + int sockfd, portno; + ssize_t n; + int reqlen = 256; + int rsplen = 256; + int ntimes = 1; + char *buffer = NULL; + char *buffer1; + char *buffer2; + char *buffer3; + struct addrinfo *ares = NULL, ahints; + struct addrinfo *altres = NULL; + int retval = 0; + int which_buf = 0; + sae_connid_t cid1, cid2; + int iter; + int bytes_to_rdwr; + int ch; + const char *host_arg = NULL; + const char *port_arg = NULL; + const char *reqlen_arg = "256"; + const char *rsplen_arg = "256"; + const char *ntimes_arg = "1"; + const char *alt_addr_arg = NULL; + const char *alt_port_arg = "0"; + int gotopt = 0; + + thiszone = gmt2local(0); + + while ((ch = getopt_long(argc, argv, "a:c:hn:p:qr:R:v", longopts, NULL)) != -1) { + gotopt = 1; + switch (ch) { + case 'a': + alt_addr_arg = optarg; + break; + case 'c': + host_arg = optarg; + break; + case 'n': + ntimes_arg = optarg; + break; + case 'p': + port_arg = optarg; + break; + case 'q': + verbose--; + break; + case 'r': + reqlen_arg = optarg; + break; + case 'R': + rsplen_arg = optarg; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(EX_USAGE); + } + } + + if (gotopt == 0) { + if (argc == 12) { + host_arg = argv[1]; + port_arg = argv[2]; + reqlen_arg = argv[3]; + rsplen_arg = argv[4]; + ntimes_arg = argv[5]; + alt_addr_arg = argv[6]; + } else { + usage(argv[0]); + exit(EX_USAGE); + } + } + + if (host_arg == NULL) + errx(EX_USAGE, "missing required host option\n"); + + if (port_arg == NULL) + errx(EX_USAGE, "missing required port option\n"); + portno = atoi(port_arg); + if (portno < 0 || portno > 65535) + errx(EX_USAGE, "invalid port %s\n", port_arg); + + if (reqlen_arg != NULL) { + reqlen = atoi(reqlen_arg); + if (reqlen < 0 || reqlen > 1024 * 1024) + errx(EX_USAGE, "invalid request length %s\n", reqlen_arg); + } + + if (rsplen_arg != NULL) { + rsplen = atoi(rsplen_arg); + if (rsplen < 0 || rsplen > 1024 * 1024) + errx(EX_USAGE, "invalid response length %s\n", rsplen_arg); + } + + if (ntimes_arg != NULL) { + ntimes = atoi(ntimes_arg); + if (ntimes < 1) + errx(EX_USAGE, "invalid ntimes option %s\n", ntimes_arg); + } + + buffer1 = setup_buffer1(reqlen); + if (!buffer1) { + printf("client: failed to alloc buffer space \n"); + return -1; + } + + buffer2 = setup_buffer2(reqlen); + if (!buffer2) { + printf("client: failed to alloc buffer space \n"); + return -1; + } + + buffer3 = setup_buffer3(rsplen); + if (!buffer3) { + printf("client: failed to alloc buffer space \n"); + return -1; + } + + if (verbose > 0) + printf("host: %s port: %s reqlen: %d rsplen: %d ntimes: %d alt_addr: %s\n", + host_arg, port_arg, reqlen, rsplen, ntimes, alt_addr_arg); + + sockfd = socket(AF_MULTIPATH, SOCK_STREAM, 0); + if (sockfd < 0) + err(EX_OSERR, "ERROR opening socket"); + + memset(&ahints, 0, sizeof(struct addrinfo)); + ahints.ai_family = AF_INET; + ahints.ai_socktype = SOCK_STREAM; + ahints.ai_protocol = IPPROTO_TCP; + + retval = getaddrinfo(host_arg, port_arg, &ahints, &ares); + if (retval != 0) + printf("getaddrinfo(%s, %s) failed %d\n", host_arg, port_arg, retval); + + bytes_to_rdwr = reqlen; + + cid1 = cid2 = SAE_CONNID_ANY; + int ifscope = 0; + int error = 0; + + if (verbose > 0) { + char str[2 * INET6_ADDRSTRLEN]; + + ts_print(); + + sprint_sockaddr(str, sizeof(str), ares->ai_addr); + printf("connectx(%s, %d, %d)\n", str, ifscope, cid1); + } + sa_endpoints_t sa; + bzero(&sa, sizeof(sa)); + sa.sae_dstaddr = ares->ai_addr; + sa.sae_dstaddrlen = ares->ai_addrlen; + sa.sae_srcif = ifscope; + + error = connectx(sockfd, &sa, SAE_ASSOCID_ANY, 0, NULL, 0, NULL, &cid1); + if (error != 0) + err(EX_OSERR, "ERROR connecting"); + + iter = 0; + + while (ntimes) { + if (iter == 0) { + /* Add alternate path if available */ + + if (alt_addr_arg && alt_addr_arg[0] != 0) { + retval = getaddrinfo(alt_addr_arg, alt_port_arg, &ahints, &altres); + + if (retval != 0) + printf("client: alternate address resolution failed. \n"); + else { + printf("client: connecting to alternate address (ifscope %d)\n", ifscope); + + if (verbose > 0) { + char str[2 * INET6_ADDRSTRLEN]; + + ts_print(); + + sprint_sockaddr(str, sizeof(str), altres->ai_addr); + printf("connectx(%s, %d, %d)\n", str, ifscope, cid1); + } + sa_endpoints_t sa; + bzero(&sa, sizeof(sa)); + sa.sae_srcif = ifscope; + sa.sae_srcaddr = altres->ai_addr; + sa.sae_srcaddrlen = altres->ai_addrlen; + sa.sae_dstaddr = ares->ai_addr; + sa.sae_dstaddrlen = ares->ai_addrlen; + + error = connectx(sockfd, &sa, SAE_ASSOCID_ANY, 0, NULL, 0, NULL, &cid2); + if (error < 0) { + err(EX_OSERR, "ERROR setting up alternate path"); + } + } + } + } + + if (which_buf == 0) { + buffer = buffer1; + which_buf = 1; + } else { + buffer = buffer2; + which_buf = 0; + } + + while (bytes_to_rdwr) { + if (verbose) { + ts_print(); + printf("writing %d bytes\n", bytes_to_rdwr); + } + n = write(sockfd, buffer, bytes_to_rdwr); + if (n <= 0) { + err(EX_OSERR, "ERROR writing to socket"); + } + if (n <= bytes_to_rdwr) + bytes_to_rdwr -= n; + else { + errx(EX_DATAERR, "ERROR extra data write %zd %d\n", n, bytes_to_rdwr); + } + } + bytes_to_rdwr = rsplen; + while (bytes_to_rdwr) { + if (verbose) { + ts_print(); + printf("reading %d bytes\n", rsplen); + } + n = read(sockfd, buffer3, rsplen); + + if (n <= 0) { + err(EX_OSERR, "ERROR reading from socket"); + } + if (n <= bytes_to_rdwr) + bytes_to_rdwr -= n; + else { + errx(EX_DATAERR, "ERROR extra bytes read n:%zd expected:%d\n", n, bytes_to_rdwr); + } + } + bytes_to_rdwr = reqlen; + ntimes--; + iter++; + } + + printf("client: Req size %d Rsp size %d Read/Write %d times \n", reqlen, rsplen, iter); + + showmpinfo(sockfd); + + if (verbose) { + ts_print(); + printf("close(%d)\n", sockfd); + } + close(sockfd); + + freeaddrinfo(ares); + if (altres) + freeaddrinfo(altres); + return 0; +} + +#define CIF_BITS \ +"\020\1CONNECTING\2CONNECTED\3DISCONNECTING\4DISCONNECTED\5BOUND_IF"\ +"\6BOUND_IP\7BOUND_PORT\10PREFERRED\11MP_CAPABLE\12MP_READY" \ +"\13MP_DEGRADED" + +/* + * Print a value a la the %b format of the kernel's printf + */ +static void +printb(const char *s, unsigned v, const char *bits) +{ + int i, any = 0; + char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++) != '\0') { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else { + for (; *bits > 32; bits++) + ; + } + } + putchar('>'); + } +} + +static int +showconninfo(int s, sae_connid_t cid) +{ + char buf[INET6_ADDRSTRLEN]; + conninfo_t *cfo = NULL; + int err; + + err = copyconninfo(s, cid, &cfo); + if (err != 0) { + printf("getconninfo failed for cid %d\n", cid); + goto out; + } + + printf("%6d:\t", cid); + printb("flags", cfo->ci_flags, CIF_BITS); + printf("\n"); + + if (cfo->ci_src != NULL) { + printf("\tsrc %s port %d\n", inet_ntop(cfo->ci_src->sa_family, + (cfo->ci_src->sa_family == AF_INET) ? + (void *)&((struct sockaddr_in *)cfo->ci_src)-> + sin_addr.s_addr : + (void *)&((struct sockaddr_in6 *)cfo->ci_src)->sin6_addr, + buf, sizeof (buf)), + (cfo->ci_src->sa_family == AF_INET) ? + ntohs(((struct sockaddr_in *)cfo->ci_src)->sin_port) : + ntohs(((struct sockaddr_in6 *)cfo->ci_src)->sin6_port)); + } + if (cfo->ci_dst != NULL) { + printf("\tdst %s port %d\n", inet_ntop(cfo->ci_dst->sa_family, + (cfo->ci_dst->sa_family == AF_INET) ? + (void *)&((struct sockaddr_in *)cfo->ci_dst)-> + sin_addr.s_addr : + (void *)&((struct sockaddr_in6 *)cfo->ci_dst)->sin6_addr, + buf, sizeof (buf)), + (cfo->ci_dst->sa_family == AF_INET) ? + ntohs(((struct sockaddr_in *)cfo->ci_dst)->sin_port) : + ntohs(((struct sockaddr_in6 *)cfo->ci_dst)->sin6_port)); + } + if (cfo->ci_aux_data != NULL) { + switch (cfo->ci_aux_type) { + case CIAUX_TCP: + printf("\tTCP aux info available\n"); + break; + default: + printf("\tUnknown aux type %d\n", cfo->ci_aux_type); + break; + } + } +out: + if (cfo != NULL) + freeconninfo(cfo); + + return (err); +} + +static void +showmpinfo(int s) +{ + uint32_t aid_cnt = 0, cid_cnt = 0; + sae_associd_t *aid = NULL; + sae_connid_t *cid = NULL; + int i, error = 0; + + error = copyassocids(s, &aid, &aid_cnt); + if (error != 0) { + printf("copyassocids failed\n"); + goto done; + } else { + printf("found %d associations", aid_cnt); + if (aid_cnt > 0) { + printf(" with IDs:"); + for (i = 0; i < aid_cnt; i++) + printf(" %d\n", aid[i]); + } + printf("\n"); + } + + /* just do an association for now */ + error = copyconnids(s, SAE_ASSOCID_ANY, &cid, &cid_cnt); + if (error != 0) { + warn("getconnids failed\n"); + goto done; + } else { + printf("found %d connections", cid_cnt); + if (cid_cnt > 0) { + printf(":\n"); + for (i = 0; i < cid_cnt; i++) { + if (showconninfo(s, cid[i]) != 0) + break; + } + } + printf("\n"); + } + +done: + if (aid != NULL) + freeassocids(aid); + if (cid != NULL) + freeconnids(cid); +} diff --git a/network_cmds/mtest.tproj/COPYING b/network_cmds/mtest.tproj/COPYING new file mode 100644 index 0000000..77bafa4 --- /dev/null +++ b/network_cmds/mtest.tproj/COPYING @@ -0,0 +1,27 @@ +Copyright (c) 2007-2009 Bruce Simpson. +Copyright (c) 2000 Wilbert De Graaf. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE.
\ No newline at end of file diff --git a/network_cmds/mtest.tproj/mtest.8 b/network_cmds/mtest.tproj/mtest.8 new file mode 100644 index 0000000..b618cc0 --- /dev/null +++ b/network_cmds/mtest.tproj/mtest.8 @@ -0,0 +1,175 @@ +.\" +.\" Copyright (c) 2007-2009 Bruce Simpson. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.sbin/mtest/mtest.8,v 1.11 2009/04/29 09:50:04 bms Exp $ +.\" +.Dd April 29, 2009 +.Os +.Dt MTEST 8 +.Sh NAME +.Nm mtest +.Nd test multicast socket operations +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility +is a small program for testing multicast socket operations. +.Pp +It accepts the following commands, interactively, or as part of a scripted +input file (useful for automated testing): +.Bl -tag -width "a ifname e.e.e.e e.e.e.e" -compact -offset indent +.Pp +.\" +.It Ic a Ar ifname Ar mac-addr +Join the link-layer group address +.Ar mac-addr +on interface +.Ar ifname . +The group address should be in IEEE 802 MAC format, +delimited by colon (':') characters. +.It Ic d Ar ifname Ar mac-addr +Leave the link-layer group address +.Ar mac-addr +on interface +.Ar ifname . +.It Ic m Ar ifname Ar 1/0 +Set or reset ALLMULTI mode on interface +.Ar ifname . +This option is deprecated and is now a no-op. +.\".It Ic p Ar ifname Ar 1/0 +.\"Set or reset promiscuous mode on interface +.\".Ar ifname . +.Pp +.It Ic j Ar mcast-addr Ar ifname Op Ar source-addr +Join the multicast address +.Ar mcast-addr +on the interface with name +.Ar ifname . +.Pp +If an optional source +.Ar source-addr +is specified, a source-specific join will be performed; +if +.Nm +is already joined to the multicast address, the source +will be added to its filter list. +.Pp +.It Ic l Ar mcast-addr Ar ifname Op Ar source-addr +Leave the multicast address +.Ar mcast-addr +on the interface with address +.Ar ifname . +If a source +.Ar source-addr +is specified, only that source will be left. +.\" +.It Ic i Ar mcast-addr Ar ifname Ar n Ar source-addr ... +Set the socket with membership of +.Ar mcast-addr +on interface +.Ar ifname +to include filter mode, and add +.Ar n +sources beginning with +.Ar source-addr +to the inclusion filter list. +.\" +.It Ic e Ar mcast-addr Ar ifname Ar n Ar source-addr ... +Set the socket with membership of +.Ar mcast-addr +on interface +.Ar ifname +to exclude filter mode, and add +.Ar n +sources beginning with +.Ar source-addr +to the exclusion filter list. +.\" +.It Ic t Ar mcast-addr Ar ifname Ar source-addr +Set the socket with membership of +.Ar mcast-addr +on interface +.Ar ifname +to block traffic from source +.Ar source-addr . +.\" +.It Ic b Ar mcast-addr Ar ifname Ar source-addr +Set the socket with membership of +.Ar mcast-addr +on interface +.Ar ifname +to allow traffic from source +.Ar source-addr . +.\" +.Pp +.It Ic g Ar mcast-addr Ar ifname Ar n +Print +.Ar n +source filter entries for +.An mcast-addr +on interface +.An ifname . +.\" +.Pp +.It Ic f Ar filename +Read commands from the file +.Ar filename . +.It Ic s Ar n +Sleep for +.Ar n +seconds. +.It Ic ?\& +List legal commands. +.It Ic q +Quit the program. +.El +.Sh IMPLEMENTATION NOTES +For each command implemented by +.Nm , +the address family of each argument must be identical; it is not possible +to mix IPv4 multicast memberships with IPv6, for example. +.Pp +To support IPv6, all commands have now changed to accept an interface +name rather than an interface address. +For IPv4, the program will perform +a lookup of the primary IP address based on the interface name. +This may fail if no primary IP address is assigned. +.Pp +.Sh SEE ALSO +.Rs +.%A D. Thaler +.%A B. Fenner +.%A B. Quinn +.%T "Socket Interface Extensions for Multicast Filters" +.%O RFC 3678 +.Re +.Sh AUTHORS +.An -split +.An "Bruce Simpson" +.An "Steve Deering" +.An "Wilbert De Graaf" diff --git a/network_cmds/mtest.tproj/mtest.c b/network_cmds/mtest.tproj/mtest.c new file mode 100644 index 0000000..9c1614e --- /dev/null +++ b/network_cmds/mtest.tproj/mtest.c @@ -0,0 +1,825 @@ +/*- + * Copyright (c) 2007-2009 Bruce Simpson. + * Copyright (c) 2000 Wilbert De Graaf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Diagnostic and test utility for multicast sockets. + * TODO: Support embedded KAME Scope ID in IPv6 group addresses. + * TODO: Use IPv4 link-local address when source address selection + * is implemented; use MCAST_JOIN_SOURCE for IPv4. + */ + +#define INET6 + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/ethernet.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#ifdef INET6 +#include <netinet/in.h> +#include <netinet/ip6.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <ifaddrs.h> + +#ifdef IP_ADD_SOURCE_MEMBERSHIP +#define HAS_SSM 1 +#endif + +union sockunion { + struct sockaddr_storage ss; + struct sockaddr sa; + struct sockaddr_dl sdl; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif +}; +typedef union sockunion sockunion_t; + +union mrequnion { + struct ip_mreq mr; +#ifdef HAS_SSM + struct ip_mreq_source mrs; +#endif +#ifdef INET6 + struct ipv6_mreq mr6; +#ifdef HAS_SSM + struct group_source_req gr; +#endif +#endif +}; +typedef union mrequnion mrequnion_t; + +#define MAX_ADDRS 20 +#define STR_SIZE 64 +#define LINE_LENGTH 80 + +static int __ifindex_to_primary_ip(const uint32_t, struct in_addr *); +static uint32_t parse_cmd_args(sockunion_t *, sockunion_t *, + const char *, const char *, const char *); +static void process_file(char *, int, int); +static void process_cmd(char*, int, int, FILE *); +static int su_cmp(const void *, const void *); +static void usage(void); + +/* + * Ordering predicate for qsort(). + */ +static int +su_cmp(const void *a, const void *b) +{ + const sockunion_t *sua = (const sockunion_t *)a; + const sockunion_t *sub = (const sockunion_t *)b; + + assert(sua->sa.sa_family == sub->sa.sa_family); + + switch (sua->sa.sa_family) { + case AF_INET: + return ((int)(sua->sin.sin_addr.s_addr - + sub->sin.sin_addr.s_addr)); + break; +#ifdef INET6 + case AF_INET6: + return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr, + sizeof(struct in6_addr))); + break; +#endif + default: + break; + } + + assert(sua->sa.sa_len == sub->sa.sa_len); + return (memcmp(sua, sub, sua->sa.sa_len)); +} + +/* + * Internal: Map an interface index to primary IPv4 address. + * This is somewhat inefficient. This is a useful enough operation + * that it probably belongs in the C library. + * Return zero if found, -1 on error, 1 on not found. + */ +static int +__ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina) +{ + char ifname[IFNAMSIZ]; + struct ifaddrs *ifa; + struct ifaddrs *ifaddrs; + sockunion_t *psu; + int retval; + + assert(ifindex != 0); + + retval = -1; + if (if_indextoname(ifindex, ifname) == NULL) + return (retval); + if (getifaddrs(&ifaddrs) < 0) + return (retval); + + /* + * Find the ifaddr entry corresponding to the interface name, + * and return the first matching IPv4 address. + */ + retval = 1; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, ifname) != 0) + continue; + psu = (sockunion_t *)ifa->ifa_addr; + if (psu && psu->sa.sa_family == AF_INET) { + retval = 0; + memcpy(pina, &psu->sin.sin_addr, + sizeof(struct in_addr)); + break; + } + } + + if (retval != 0) + errno = EADDRNOTAVAIL; /* XXX */ + + freeifaddrs(ifaddrs); + return (retval); +} + +int +main(int argc, char **argv) +{ + char line[LINE_LENGTH]; + char *p; + int i, s, s6; + + s = -1; + s6 = -1; + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) + err(1, "can't open IPv4 socket"); +#ifdef INET6 + s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s6 == -1) + err(1, "can't open IPv6 socket"); +#endif + + if (argc < 2) { + if (isatty(STDIN_FILENO)) { + printf("multicast membership test program; " + "enter ? for list of commands\n"); + } + do { + if (fgets(line, sizeof(line), stdin) != NULL) { + if (line[0] != 'f') + process_cmd(line, s, s6, stdin); + else { + /* Get the filename */ + for (i = 1; isblank(line[i]); i++); + if ((p = (char*)strchr(line, '\n')) + != NULL) + *p = '\0'; + process_file(&line[i], s, s6); + } + } + } while (!feof(stdin)); + } else { + for (i = 1; i < argc; i++) { + process_file(argv[i], s, s6); + } + } + + if (s != -1) + close(s); + if (s6 != -1) + close(s6); + + exit (0); +} + +static void +process_file(char *fname, int s, int s6) +{ + char line[80]; + FILE *fp; + char *lineptr; + + fp = fopen(fname, "r"); + if (fp == NULL) { + warn("fopen"); + return; + } + + /* Skip comments and empty lines. */ + while (fgets(line, sizeof(line), fp) != NULL) { + lineptr = line; + while (isblank(*lineptr)) + lineptr++; + if (*lineptr != '#' && *lineptr != '\n') + process_cmd(lineptr, s, s6, fp); + } + + fclose(fp); +} + +/* + * Parse join/leave/allow/block arguments, given: + * str1: group (as AF_INET or AF_INET6 printable) + * str2: ifname + * str3: optional source address (may be NULL). + * This argument must have the same parsed address family as str1. + * Return the ifindex of ifname, or 0 if any parse element failed. + */ +static uint32_t +parse_cmd_args(sockunion_t *psu, sockunion_t *psu2, + const char *str1, const char *str2, const char *str3) +{ + struct addrinfo hints; + struct addrinfo *res; + uint32_t ifindex; + int af, error; + + assert(psu != NULL); + assert(str1 != NULL); + assert(str2 != NULL); + + af = AF_UNSPEC; + + ifindex = if_nametoindex(str2); + if (ifindex == 0) + return (0); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + memset(psu, 0, sizeof(sockunion_t)); + psu->sa.sa_family = AF_UNSPEC; + + error = getaddrinfo(str1, "0", &hints, &res); + if (error) { + warnx("getaddrinfo: %s", gai_strerror(error)); + return (0); + } + assert(res != NULL); + af = res->ai_family; + memcpy(psu, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + + /* sscanf() may pass the empty string. */ + if (psu2 != NULL && str3 != NULL && *str3 != '\0') { + memset(psu2, 0, sizeof(sockunion_t)); + psu2->sa.sa_family = AF_UNSPEC; + + /* look for following address family; str3 is *optional*. */ + hints.ai_family = af; + error = getaddrinfo(str3, "0", &hints, &res); + if (error) { + warnx("getaddrinfo: %s", gai_strerror(error)); + ifindex = 0; + } else { + if (af != res->ai_family) { + errno = EINVAL; /* XXX */ + ifindex = 0; + } + memcpy(psu2, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } + } + + return (ifindex); +} + +static __inline int +af2sock(const int af, int s, int s6) +{ + + if (af == AF_INET) + return (s); +#ifdef INET6 + if (af == AF_INET6) + return (s6); +#endif + return (-1); +} + +static void +process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused) +{ + char str1[STR_SIZE]; + char str2[STR_SIZE]; + char str3[STR_SIZE]; + mrequnion_t mr; + sockunion_t su, su2; + struct ifreq ifr; + char *line; + char *toptname; + void *optval; + uint32_t fmode, ifindex; + socklen_t optlen; + int af, error, i, level, n = 0, optname; +#ifndef __APPLE__ + int f, flags; +#endif /* __APPLE__ */ + + af = AF_UNSPEC; + su.sa.sa_family = AF_UNSPEC; + su2.sa.sa_family = AF_UNSPEC; + + line = cmd; + while (isblank(*++line)) + ; /* Skip whitespace. */ + + switch (*cmd) { + case '?': + usage(); + break; + + case 'q': + close(s); + exit(0); + + case 's': + if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { + printf("-1\n"); + break; + } + sleep(n); + printf("ok\n"); + break; + + case 'j': + case 'l': + str3[0] = '\0'; + toptname = ""; + sscanf(line, "%s %s %s", str1, str2, str3); + ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); + if (ifindex == 0) { + printf("-1\n"); + break; + } + af = su.sa.sa_family; + if (af == AF_INET) { + struct in_addr ina; + + error = __ifindex_to_primary_ip(ifindex, &ina); + if (error != 0) { + warn("primary_ip_lookup %s", str2); + printf("-1\n"); + break; + } + level = IPPROTO_IP; + +#ifdef HAS_SSM + if (su2.sa.sa_family != AF_UNSPEC) { + mr.mrs.imr_multiaddr = su.sin.sin_addr; + mr.mrs.imr_sourceaddr = su2.sin.sin_addr; + mr.mrs.imr_interface = ina; + optname = (*cmd == 'j') ? + IP_ADD_SOURCE_MEMBERSHIP : + IP_DROP_SOURCE_MEMBERSHIP; + toptname = (*cmd == 'j') ? + "IP_ADD_SOURCE_MEMBERSHIP" : + "IP_DROP_SOURCE_MEMBERSHIP"; + optval = (void *)&mr.mrs; + optlen = sizeof(mr.mrs); + } else { +#endif + mr.mr.imr_multiaddr = su.sin.sin_addr; + mr.mr.imr_interface = ina; + optname = (*cmd == 'j') ? + IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + toptname = (*cmd == 'j') ? + "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP"; + optval = (void *)&mr.mr; + optlen = sizeof(mr.mr); +#ifdef HAS_SSM + } +#endif + if (setsockopt(s, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#ifdef INET6 + else +#endif /* INET6 */ +#ifdef INET6 + if (af == AF_INET6) { + level = IPPROTO_IPV6; +#ifdef HAS_SSM + if (su2.sa.sa_family != AF_UNSPEC) { + mr.gr.gsr_interface = ifindex; + mr.gr.gsr_group = su.ss; + mr.gr.gsr_source = su2.ss; + optname = (*cmd == 'j') ? + MCAST_JOIN_SOURCE_GROUP: + MCAST_LEAVE_SOURCE_GROUP; + toptname = (*cmd == 'j') ? + "MCAST_JOIN_SOURCE_GROUP": + "MCAST_LEAVE_SOURCE_GROUP"; + optval = (void *)&mr.gr; + optlen = sizeof(mr.gr); + } else { +#endif + mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr; + mr.mr6.ipv6mr_interface = ifindex; + optname = (*cmd == 'j') ? + IPV6_JOIN_GROUP : + IPV6_LEAVE_GROUP; + toptname = (*cmd == 'j') ? + "IPV6_JOIN_GROUP" : + "IPV6_LEAVE_GROUP"; + optval = (void *)&mr.mr6; + optlen = sizeof(mr.mr6); +#ifdef HAS_SSM + } +#endif + if (setsockopt(s6, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#endif /* INET6 */ + /* FALLTHROUGH */ + printf("-1\n"); + break; + +#ifdef HAS_SSM + /* + * Set the socket to include or exclude filter mode, and + * add some sources to the filterlist, using the full-state API. + */ + case 'i': + case 'e': { + sockunion_t sources[MAX_ADDRS]; + struct addrinfo hints; + struct addrinfo *res; + char *cp; + int af1; + + n = 0; + fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE; + if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { + printf("-1\n"); + break; + } + + ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); + if (ifindex == 0 || n < 0 || n > MAX_ADDRS) { + printf("-1\n"); + break; + } + af = su.sa.sa_family; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; + + for (i = 0; i < n; i++) { + sockunion_t *psu = (sockunion_t *)&sources[i]; + /* + * Trim trailing whitespace, as getaddrinfo() + * can't cope with it. + */ + fgets(str1, sizeof(str1), fp); + cp = strchr(str1, '\n'); + if (cp != NULL) + *cp = '\0'; + + res = NULL; + error = getaddrinfo(str1, "0", &hints, &res); + if (error) + break; + assert(res != NULL); + + memset(psu, 0, sizeof(sockunion_t)); + af1 = res->ai_family; + if (af1 == af) + memcpy(psu, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + if (af1 != af) + break; + } + if (i < n) { + if (error) + warnx("getaddrinfo: %s", gai_strerror(error)); + printf("-1\n"); + break; + } + if (setsourcefilter(af2sock(af, s, s6), ifindex, + &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0) + warn("setsourcefilter"); + else + printf("ok\n"); + } break; + + /* + * Allow or block traffic from a source, using the + * delta based api. + */ + case 't': + case 'b': { + str3[0] = '\0'; + toptname = ""; + sscanf(line, "%s %s %s", str1, str2, str3); + ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); + if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) { + printf("-1\n"); + break; + } + af = su.sa.sa_family; + + /* First determine our current filter mode. */ + n = 0; + if (getsourcefilter(af2sock(af, s, s6), ifindex, + &su.sa, su.sa.sa_len, &fmode, (uint32_t *)&n, NULL) != 0) { + warn("getsourcefilter"); + break; + } + if (af == AF_INET) { + struct in_addr ina; + + error = __ifindex_to_primary_ip(ifindex, &ina); + if (error != 0) { + warn("primary_ip_lookup %s", str2); + printf("-1\n"); + break; + } + level = IPPROTO_IP; + optval = (void *)&mr.mrs; + optlen = sizeof(mr.mrs); + mr.mrs.imr_multiaddr = su.sin.sin_addr; + mr.mrs.imr_sourceaddr = su2.sin.sin_addr; + mr.mrs.imr_interface = ina; + if (fmode == MCAST_EXCLUDE) { + /* Any-source mode socket membership. */ + optname = (*cmd == 't') ? + IP_UNBLOCK_SOURCE : + IP_BLOCK_SOURCE; + toptname = (*cmd == 't') ? + "IP_UNBLOCK_SOURCE" : + "IP_BLOCK_SOURCE"; + } else { + /* Source-specific mode socket membership. */ + optname = (*cmd == 't') ? + IP_ADD_SOURCE_MEMBERSHIP : + IP_DROP_SOURCE_MEMBERSHIP; + toptname = (*cmd == 't') ? + "IP_ADD_SOURCE_MEMBERSHIP" : + "IP_DROP_SOURCE_MEMBERSHIP"; + } + if (setsockopt(s, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#ifdef INET6 + else +#endif /* INET6 */ +#ifdef INET6 + if (af == AF_INET6) { + level = IPPROTO_IPV6; + mr.gr.gsr_interface = ifindex; + mr.gr.gsr_group = su.ss; + mr.gr.gsr_source = su2.ss; + if (fmode == MCAST_EXCLUDE) { + /* Any-source mode socket membership. */ + optname = (*cmd == 't') ? + MCAST_UNBLOCK_SOURCE : + MCAST_BLOCK_SOURCE; + toptname = (*cmd == 't') ? + "MCAST_UNBLOCK_SOURCE" : + "MCAST_BLOCK_SOURCE"; + } else { + /* Source-specific mode socket membership. */ + optname = (*cmd == 't') ? + MCAST_JOIN_SOURCE_GROUP : + MCAST_LEAVE_SOURCE_GROUP; + toptname = (*cmd == 't') ? + "MCAST_JOIN_SOURCE_GROUP": + "MCAST_LEAVE_SOURCE_GROUP"; + } + optval = (void *)&mr.gr; + optlen = sizeof(mr.gr); + if (setsockopt(s6, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#endif /* INET6 */ + /* FALLTHROUGH */ + printf("-1\n"); + } break; + + case 'g': { + sockunion_t sources[MAX_ADDRS]; + char addrbuf[NI_MAXHOST]; + int nreqsrc, nsrc; + + if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) { + printf("-1\n"); + break; + } + ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); + if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) { + printf("-1\n"); + break; + } + + af = su.sa.sa_family; + nsrc = nreqsrc; + if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa, + su.sa.sa_len, &fmode, (uint32_t *)&nsrc, + &sources[0].ss) != 0) { + warn("getsourcefilter"); + printf("-1\n"); + break; + } + printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" : + "exclude"); + printf("%d\n", nsrc); + + nsrc = MIN(nreqsrc, nsrc); + fprintf(stderr, "hexdump of sources:\n"); + uint8_t *bp = (uint8_t *)&sources[0]; + for (i = 0; i < (nsrc * sizeof(sources[0])); i++) { + fprintf(stderr, "%02x", bp[i]); + } + fprintf(stderr, "\nend hexdump\n"); + + qsort(sources, nsrc, sizeof (sockunion_t), su_cmp); + for (i = 0; i < nsrc; i++) { + sockunion_t *psu = (sockunion_t *)&sources[i]; + addrbuf[0] = '\0'; + error = getnameinfo(&psu->sa, psu->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, + NI_NUMERICHOST); + if (error) + warnx("getnameinfo: %s", gai_strerror(error)); + else + printf("%s\n", addrbuf); + } + printf("ok\n"); + } break; +#endif + /* link-layer stuff follows. */ + + case 'a': + case 'd': { + struct sockaddr_dl *dlp; + struct ether_addr *ep; + + memset(&ifr, 0, sizeof(struct ifreq)); + dlp = (struct sockaddr_dl *)&ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETHER_ADDR_LEN; + dlp->sdl_slen = 0; + if (sscanf(line, "%s %s", str1, str2) != 2) { + warnc(EINVAL, "sscanf"); + break; + } + ep = ether_aton(str2); + if (ep == NULL) { + warnc(EINVAL, "ether_aton"); + break; + } + strlcpy(ifr.ifr_name, str1, sizeof(ifr.ifr_name)); + memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); + if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, + &ifr) == -1) { + warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); + printf("-1\n"); + } else + printf("ok\n"); + break; + } + + case 'm': + fprintf(stderr, + "warning: IFF_ALLMULTI cannot be set from userland " + "in Darwin; command ignored.\n"); + printf("-1\n"); + break; + +#ifndef __APPLE__ + case 'p': + if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { + printf("-1\n"); + break; + } + if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { + warn("ioctl SIOCGIFFLAGS"); + break; + } + flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); + if (f == 0) { + flags &= ~IFF_PPROMISC; + } else { + flags |= IFF_PPROMISC; + } + ifr.ifr_flags = flags & 0xffff; + ifr.ifr_flagshigh = flags >> 16; + if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) + warn("ioctl SIOCGIFFLAGS"); + else + printf( "changed to 0x%08x\n", flags ); + break; +#endif /* __APPLE__ */ + case '\n': + break; + default: + printf("invalid command\n"); + break; + } +} + +static void +usage(void) +{ + +#ifndef HAS_SSM + printf("j mcast-addr ifname - join IP multicast group\n"); + printf("l mcast-addr ifname - leave IP multicast group\n"); +#else /* HAS_SSM */ + printf("j mcast-addr ifname [src-addr] - join IP multicast group\n"); + printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n"); + printf( + "i mcast-addr ifname n - set n include mode src filter\n"); + printf( + "e mcast-addr ifname n - set n exclude mode src filter\n"); + printf("t mcast-addr ifname src-addr - allow traffic from src\n"); + printf("b mcast-addr ifname src-addr - block traffic from src\n"); + printf("g mcast-addr ifname n - get and show n src filters\n"); +#endif + printf("a ifname mac-addr - add link multicast filter\n"); + printf("d ifname mac-addr - delete link multicast filter\n"); + printf("m ifname 1/0 - set/clear ether allmulti flag\n"); +#ifndef __APPLE__ + printf("p ifname 1/0 - set/clear ether promisc flag\n"); +#endif /* __APPLE__ */ + printf("f filename - read command(s) from file\n"); + printf("s seconds - sleep for some time\n"); + printf("q - quit\n"); +} + diff --git a/network_cmds/ndp.tproj/gnuc.h b/network_cmds/ndp.tproj/gnuc.h new file mode 100644 index 0000000..5c6b29e --- /dev/null +++ b/network_cmds/ndp.tproj/gnuc.h @@ -0,0 +1,2 @@ +/* $FreeBSD: src/usr.sbin/ndp/gnuc.h,v 1.1 2000/01/06 12:40:40 shin Exp $ */ +/* this is dummy to pacify gmt2local.c. */ diff --git a/network_cmds/ndp.tproj/ndp.8 b/network_cmds/ndp.tproj/ndp.8 new file mode 100644 index 0000000..b1217eb --- /dev/null +++ b/network_cmds/ndp.tproj/ndp.8 @@ -0,0 +1,236 @@ +.\" Copyright (c) 2012-2013 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd May 17, 1998 +.Dt NDP 8 +.Os +.\" +.Sh NAME +.Nm ndp +.Nd control/diagnose IPv6 neighbor discovery protocol +.\" +.Sh SYNOPSIS +.Nm +.Fl a +.Op Fl lnt +.Nm +.Fl A Ar wait +.Op Fl nt +.Nm +.Fl c +.Op Fl nt +.Nm +.Fl d +.Op Fl nt +.Ar hostname +.Nm +.Fl f +.Op Fl nt +.Ar filename +.Nm +.Fl H +.Nm +.Fl I +.Op Cm delete | Ar interface +.Nm +.Fl i +.Ar interface +.Op Ar flags... +.Nm +.Fl p +.Nm +.Fl P +.Nm +.Fl r +.Nm +.Fl R +.Nm +.Fl s +.Op Fl nt +.Ar nodename +.Ar ether_addr +.Op Li temp +.Op Li proxy +.\" +.Sh DESCRIPTION +The +.Nm +command manipulates the address mapping table +used by Neighbor Discovery Protocol (NDP). +.Bl -tag -width Ds +.It Fl a +Dump the currently existing NDP entries. +.It Fl A Ar wait +Repeat +.Fl a +(dump NDP entries) +every +.Ar wait +seconds. +.It Fl c +Erase all the NDP entries. +.It Fl d +Delete specified NDP entry. +.It Fl f +Parse the file specified by +.Ar filename . +.It Fl H +Harmonize consistency between the routing table and the default router +list; install the top entry of the list into the kernel routing table. +.It Fl I Op Cm delete | Ar interface +Shows or specifies the default interface used as the default route when +there is no default router. +If no argument is given to the option, +the current default interface will be shown. +If an +.Ar interface +is specified, the interface will be used as the default. +If a special keyword +.Ic delete +is specified, the current default interface will be deleted from the kernel. +.It Fl i Ar interface Op Ar flags... +View ND information for the specified interface. +If additional arguments +.Ar flags +are given, +.Nm +sets or clears the specified flags for the interface. +Possible flags are as follows. +All of the flags can begin with the +special character +.Ql - , +which means the flag should be cleared. +.\" +.Bl -tag -width Ds -compact +.It Xo +.Ic nud +.Xc +turn on or off NUD (Neighbor Unreachability Detection) on the +interface. +NUD is usually turned on by default. +.It Xo +.Ic disabled +.Xc +IPv6 can be disabled separately from other network protocols. This flag can be +turned on automatically when Duplicate Address Detection (DAD) indicates that +another device on the network is using the same link-local address. +.It Xo +.Ic proxy_prefixes +.Xc +the interface is enabled to proxy neighbor discovery for global scope prefixes +matching those on link at other interfaces. +.It Xo +.Ic insecure +do not use cryptographically generated addresses (CGA) on this interface. +.Xc +.It Xo +.Ic replicated +Address autoconfiguration proceeds under the assumption that interface +configuration is replicated by a sleep proxy at another node on the link. +Disables optimistic DAD and sends unsolicited NA with O=1 when DAD completes. +Ignores DAD failures from other hardware addresses. +.Xc +.El +.It Fl l +Show link-layer reachability information. +.It Fl n +Do not try to resolve numeric address to hostname. +.It Fl p +Show prefix list. +.It Fl P +Flush all the entries in the prefix list. +.It Fl r +Show default router list. +.It Fl R +Flush all the entries in the default router list. +.It Fl s +Register an NDP entry for a node. +The entry will be permanent unless the word +.Li temp +is given in the command. +If the word +.Li proxy +is given, this system will act as an proxy NDP server, +responding to requests for +.Ar hostname +even though the host address is not its own. +.It Fl t +Print timestamp on each entries, +to make it possible to merge output with +.Xr tcpdump 1 . +Most useful when used with +.Fl A . +.It Fl x +Show extended link-layer reachability information in addition to that shown by +the +.Fl l +flag. +.It Fl w +Show the cryptographically generated address (CGA) parameters for the node. +.El +.\" +.Sh RETURN VALUES +The +.Nm +command will exit with 0 on success, and non-zero on errors. +.\" +.Sh SEE ALSO +.Xr arp 8 +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" +.\" .Sh BUGS +.\" (to be written) diff --git a/network_cmds/ndp.tproj/ndp.c b/network_cmds/ndp.tproj/ndp.c new file mode 100644 index 0000000..b485fa1 --- /dev/null +++ b/network_cmds/ndp.tproj/ndp.c @@ -0,0 +1,1696 @@ +/* + * Copyright (c) 2009-2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Sun Microsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on: + * "@(#) Copyright (c) 1984, 1993\n\ + * The Regents of the University of California. All rights reserved.\n"; + * + * "@(#)arp.c 8.2 (Berkeley) 1/2/94"; + */ + +/* + * ndp - display, set, delete and flush neighbor cache + */ + +#include <stdint.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <netinet/icmp6.h> +#include <netinet6/in6_var.h> +#include <netinet6/nd6.h> + +#include <arpa/inet.h> + +#include <netdb.h> +#include <errno.h> +#include <nlist.h> +#include <stdio.h> +#include <string.h> +#include <paths.h> +#include <err.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> + +/* packing rule for routing socket */ +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof (uint32_t) - 1))) : \ + sizeof (uint32_t)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +static int pid; +static int cflag; +static int nflag; +static int tflag; +static int32_t thiszone = 0; /* time difference with gmt */ +static int s = -1; +static int repeat = 0; + +static char host_buf[NI_MAXHOST]; /* getnameinfo() */ +static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ + +static int file(char *); +static void getsocket(void); +static int set(int, char **); +static void get(char *); +static int delete(char *); +static void dump(struct in6_addr *); +static void dump_ext(struct in6_addr *, int); +static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); +static char *ether_str(struct sockaddr_dl *); +static int ndp_ether_aton(char *, u_char *); +static void usage(void); +static int rtmsg(int); +static void ifinfo(int, char **); +static void rtrlist(void); +static void plist(void); +static void pfx_flush(void); +static void rtrlist(void); +static void rtr_flush(void); +static void harmonize_rtr(void); +static void getdefif(void); +static void setdefif(char *); +static char *sec2str(time_t); +static char *ether_str(struct sockaddr_dl *); +static void ts_print(const struct timeval *); +static void read_cga_parameters(void); +static void write_cga_parameters(const char[]); + +static char *rtpref_str[] = { + "medium", /* 00 */ + "high", /* 01 */ + "rsv", /* 10 */ + "low" /* 11 */ +}; + +int +main(int argc, char **argv) +{ + int ch; + int aflag = 0, dflag = 0, sflag = 0, Hflag = 0, pflag = 0, rflag = 0, + Pflag = 0, Rflag = 0, lflag = 0, xflag = 0, wflag = 0; + + pid = getpid(); + while ((ch = getopt(argc, argv, "acndfIilprstA:HPRxwW")) != -1) + switch ((char) ch) { + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'I': + if (argc > 2) + setdefif(argv[2]); + getdefif(); /* always call it to print the result */ + exit(0); + case 'i' : + argc -= optind; + argv += optind; + if (argc < 1) + usage(); + ifinfo(argc, argv); + exit(0); + case 'n': + nflag = 1; + continue; + case 'p': + pflag = 1; + break; + case 'f' : + if (argc != 3) + usage(); + file(argv[2]); + exit(0); + case 'l' : + lflag = 1; + break; + case 'r' : + rflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = 1; + break; + case 'A': + aflag = 1; + repeat = atoi(optarg); + if (repeat < 0) + usage(); + break; + case 'H' : + Hflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'R': + Rflag = 1; + break; + case 'x': + xflag = 1; + lflag = 1; + break; + case 'w': + wflag = 1; + break; + case 'W': + if (argc != 3) + usage(); + write_cga_parameters(argv[2]); + exit(0); + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (aflag || cflag) { + if (lflag) + dump_ext(0, xflag); + else + dump(0); + exit(0); + } + if (dflag) { + if (argc != 1) + usage(); + delete(argv[0]); + exit(0); + } + if (pflag) { + plist(); + exit(0); + } + if (rflag) { + rtrlist(); + exit(0); + } + if (sflag) { + if (argc < 2 || argc > 4) + usage(); + exit(set(argc, argv) ? 1 : 0); + } + if (Hflag) { + harmonize_rtr(); + exit(0); + } + if (Pflag) { + pfx_flush(); + exit(0); + } + if (Rflag) { + rtr_flush(); + exit(0); + } + if (wflag) { + read_cga_parameters(); + exit(0); + } + + if (argc != 1) + usage(); + get(argv[0]); + exit(0); +} + +/* + * Process a file to set standard ndp entries + */ +static int +file(char *name) +{ + FILE *fp; + int i, retval; + char line[100], arg[5][50], *args[5]; + + if ((fp = fopen(name, "r")) == NULL) { + fprintf(stderr, "ndp: cannot open %s\n", name); + exit(1); + } + args[0] = &arg[0][0]; + args[1] = &arg[1][0]; + args[2] = &arg[2][0]; + args[3] = &arg[3][0]; + args[4] = &arg[4][0]; + retval = 0; + while (fgets(line, 100, fp) != NULL) { + i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2], + arg[3], arg[4]); + if (i < 2) { + fprintf(stderr, "ndp: bad line: %s\n", line); + retval = 1; + continue; + } + if (set(i, args)) + retval = 1; + } + fclose(fp); + return (retval); +} + +static void +getsocket(void) +{ + if (s < 0) { + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) { + perror("ndp: socket"); + exit(1); + } + } +} + +struct sockaddr_in6 so_mask = {sizeof (so_mask), AF_INET6 }; +struct sockaddr_in6 blank_sin = {sizeof (blank_sin), AF_INET6 }, sin_m; +struct sockaddr_dl blank_sdl = {sizeof (blank_sdl), AF_LINK }, sdl_m; +int expire_time, flags, found_entry; +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +/* + * Set an individual neighbor cache entry + */ +static int +set(int argc, char **argv) +{ + register struct sockaddr_in6 *sin = &sin_m; + register struct sockaddr_dl *sdl; + register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + struct addrinfo hints, *res; + int gai_error; + u_char *ea; + char *host = argv[0], *eaddr = argv[1]; + int ealen; + + getsocket(); + argc -= 2; + argv += 2; + sdl_m = blank_sdl; + sin_m = blank_sin; + + bzero(&hints, sizeof (hints)); + hints.ai_family = AF_INET6; + gai_error = getaddrinfo(host, NULL, &hints, &res); + if (gai_error) { + fprintf(stderr, "ndp: %s: %s\n", host, + gai_strerror(gai_error)); + return (1); + } + sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) { + *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = + htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id); + } +#endif + ea = (u_char *)LLADDR(&sdl_m); + + ealen = ndp_ether_aton(eaddr, ea); + if (ealen != -1) + sdl_m.sdl_alen = ealen; + flags = expire_time = 0; + while (argc-- > 0) { + if (strncmp(argv[0], "temp", 4) == 0) { + struct timeval time; + gettimeofday(&time, 0); + expire_time = time.tv_sec + 20 * 60; + } else if (strncmp(argv[0], "proxy", 5) == 0) + flags |= RTF_ANNOUNCE; + argv++; + } + if (rtmsg(RTM_GET) < 0) { + perror(host); + return (1); + } + sin = (struct sockaddr_in6 *)(rtm + 1); + sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin); + if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { + case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: + case IFT_ISO88024: case IFT_ISO88025: + case IFT_6LOWPAN: + goto overwrite; + } + /* + * IPv4 arp command retries with sin_other = SIN_PROXY here. + */ + fprintf(stderr, "set: cannot configure a new entry\n"); + return (1); + } + +overwrite: + if (sdl->sdl_family != AF_LINK) { + printf("cannot intuit interface index and type for %s\n", host); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(RTM_ADD)); +} + +/* + * Display an individual neighbor cache entry + */ +static void +get(char *host) +{ + struct sockaddr_in6 *sin = &sin_m; + struct addrinfo hints, *res; + int gai_error; + + sin_m = blank_sin; + bzero(&hints, sizeof (hints)); + hints.ai_family = AF_INET6; + gai_error = getaddrinfo(host, NULL, &hints, &res); + if (gai_error) { + fprintf(stderr, "ndp: %s: %s\n", host, + gai_strerror(gai_error)); + return; + } + sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) { + *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = + htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id); + } +#endif + dump(&sin->sin6_addr); + if (found_entry == 0) { + getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, + sizeof (host_buf), NULL, 0, NI_WITHSCOPEID | (nflag ? + NI_NUMERICHOST : 0)); + printf("%s (%s) -- no entry\n", host, host_buf); + exit(1); + } +} + +/* + * Delete a neighbor cache entry + */ +static int +delete(char *host) +{ + struct sockaddr_in6 *sin = &sin_m; + register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + struct sockaddr_dl *sdl; + struct addrinfo hints, *res; + int gai_error; + + getsocket(); + sin_m = blank_sin; + + bzero(&hints, sizeof (hints)); + hints.ai_family = AF_INET6; + gai_error = getaddrinfo(host, NULL, &hints, &res); + if (gai_error) { + fprintf(stderr, "ndp: %s: %s\n", host, + gai_strerror(gai_error)); + return (1); + } + sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) { + *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = + htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id); + } +#endif + if (rtmsg(RTM_GET) < 0) { + perror(host); + return (1); + } + sin = (struct sockaddr_in6 *)(rtm + 1); + sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin); + if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY)) { + goto delete; + } + /* + * IPv4 arp command retries with sin_other = SIN_PROXY here. + */ + fprintf(stderr, "delete: cannot delete non-NDP entry\n"); + return (1); + } + +delete: + if (sdl->sdl_family != AF_LINK) { + printf("cannot locate %s\n", host); + return (1); + } + if (rtmsg(RTM_DELETE) == 0) { + struct sockaddr_in6 s6 = *sin; + +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(&s6.sin6_addr)) { + s6.sin6_scope_id = + ntohs(*(u_int16_t *)&s6.sin6_addr.s6_addr[2]); + *(u_int16_t *)&s6.sin6_addr.s6_addr[2] = 0; + } +#endif + getnameinfo((struct sockaddr *)&s6, + s6.sin6_len, host_buf, + sizeof (host_buf), NULL, 0, + NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0)); + printf("%s (%s) deleted\n", host, host_buf); + } + + return (0); +} + +#define W_ADDR 31 +#define W_LL 17 +#define W_IF 6 + +/* + * Dump the entire neighbor cache + */ +static void +dump(struct in6_addr *addr) +{ + int mib[6]; + size_t needed; + char *lim, *buf, *next; + struct rt_msghdr *rtm; + struct sockaddr_in6 *sin; + struct sockaddr_dl *sdl; + struct in6_nbrinfo *nbi; + struct timeval time; + int addrwidth; + int llwidth; + int ifwidth; + char flgbuf[8]; + char *ifname; + + /* Print header */ + if (!tflag && !cflag) + printf("%-*.*s %-*.*s %*.*s %-9.9s %2s %4s %4s\n", + W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", + W_IF, W_IF, "Netif", "Expire", "St", "Flgs", "Prbs"); + +again:; + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + mib[4] = NET_RT_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(1, "sysctl(PF_ROUTE estimate)"); + if (needed > 0) { + if ((buf = malloc(needed)) == NULL) + errx(1, "malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); + lim = buf + needed; + } else + buf = lim = NULL; + + for (next = buf; next && next < lim; next += rtm->rtm_msglen) { + int isrouter = 0, prbs = 0; + + rtm = (struct rt_msghdr *)next; + sin = (struct sockaddr_in6 *)(rtm + 1); + sdl = (struct sockaddr_dl *)((char *)sin + + ROUNDUP(sin->sin6_len)); + + /* + * Some OSes can produce a route that has the LINK flag but + * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD + * and BSD/OS, where xx is not the interface identifier on + * lo0). Such routes entry would annoy getnbrinfo() below, + * so we skip them. + * XXX: such routes should have the GATEWAY flag, not the + * LINK flag. However, there are rotten routing software + * that advertises all routes that have the GATEWAY flag. + * Thus, KAME kernel intentionally does not set the LINK flag. + * What is to be fixed is not ndp, but such routing software + * (and the kernel workaround)... + */ + if (sdl->sdl_family != AF_LINK) + continue; + + if (addr) { + if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr)) + continue; + found_entry = 1; + } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) + continue; + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&sin->sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { + /* should scope id be filled in the kernel? */ + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = sdl->sdl_index; +#ifdef __KAME__ + /* KAME specific hack; removed the embedded id */ + *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0; +#endif + } + getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, + sizeof (host_buf), NULL, 0, + NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0)); + if (cflag == 1) { + if (rtm->rtm_flags & RTF_WASCLONED) + delete(host_buf); + continue; + } + gettimeofday(&time, 0); + if (tflag) + ts_print(&time); + + addrwidth = strlen(host_buf); + if (addrwidth < W_ADDR) + addrwidth = W_ADDR; + llwidth = strlen(ether_str(sdl)); + if (W_ADDR + W_LL - addrwidth > llwidth) + llwidth = W_ADDR + W_LL - addrwidth; + ifname = if_indextoname(sdl->sdl_index, ifix_buf); + if (!ifname) + ifname = "?"; + ifwidth = strlen(ifname); + if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) + ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; + + printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, + llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); + + /* Print neighbor discovery specific informations */ + nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1); + if (nbi) { + if (nbi->expire > time.tv_sec) { + printf(" %-9.9s", sec2str(nbi->expire - + time.tv_sec)); + } else if (nbi->expire == 0) + printf(" %-9.9s", "permanent"); + else + printf(" %-9.9s", "expired"); + + switch (nbi->state) { + case ND6_LLINFO_NOSTATE: + printf(" N"); + break; + case ND6_LLINFO_INCOMPLETE: + printf(" I"); + break; + case ND6_LLINFO_REACHABLE: + printf(" R"); + break; + case ND6_LLINFO_STALE: + printf(" S"); + break; + case ND6_LLINFO_DELAY: + printf(" D"); + break; + case ND6_LLINFO_PROBE: + printf(" P"); + break; + default: + printf(" ?"); + break; + } + + isrouter = nbi->isrouter; + prbs = nbi->asked; + } else { + warnx("failed to get neighbor information"); + printf(" "); + } + putchar(' '); + + /* + * other flags. R: router, P: proxy, W: ?? + */ + if ((rtm->rtm_addrs & RTA_NETMASK) == 0) { + snprintf(flgbuf, sizeof (flgbuf), "%s%s", + isrouter ? "R" : "", + (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); + } else { + sin = (struct sockaddr_in6 *) + (sdl->sdl_len + (char *)sdl); + snprintf(flgbuf, sizeof (flgbuf), "%s%s%s%s", + isrouter ? "R" : "", + !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) + ? "P" : "", + (sin->sin6_len != sizeof (struct sockaddr_in6)) + ? "W" : "", + (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); + } + printf(" %-4.4s", flgbuf); + + if (prbs) + printf(" %4d", prbs); + + printf("\n"); + } + if (buf != NULL) + free(buf); + + if (repeat) { + printf("\n"); + sleep(repeat); + goto again; + } +} + +/* + * Dump the entire neighbor cache (extended) + */ +void +dump_ext(addr, xflag) + struct in6_addr *addr; + int xflag; +{ + int mib[6]; + size_t needed; + char *lim, *buf, *next; + struct rt_msghdr_ext *ertm; + struct sockaddr_in6 *sin; + struct sockaddr_dl *sdl; + struct in6_nbrinfo *nbi; + struct timeval time; + int addrwidth; + int llwidth; + int ifwidth; + char flgbuf[8]; + char *ifname; + + /* Print header */ + if (!tflag && !cflag) { + printf("%-*.*s %-*.*s %*.*s %-9.9s %-9.9s %2s %4s %4s", + W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", + W_IF, W_IF, "Netif", "Expire(O)", "Expire(I)", "St", + "Flgs", "Prbs"); + if (xflag) + printf(" %-7.7s %-7.7s %-7.7s", "RSSI", "LQM", "NPM"); + printf("\n"); + } + +again:; + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + mib[4] = NET_RT_DUMPX_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(1, "sysctl(PF_ROUTE estimate)"); + if (needed > 0) { + if ((buf = malloc(needed)) == NULL) + errx(1, "malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); + lim = buf + needed; + } else + buf = lim = NULL; + + for (next = buf; next && next < lim; next += ertm->rtm_msglen) { + int isrouter = 0, prbs = 0; + + ertm = (struct rt_msghdr_ext *)next; + sin = (struct sockaddr_in6 *)(ertm + 1); + sdl = (struct sockaddr_dl *)((char *)sin + + ROUNDUP(sin->sin6_len)); + + /* + * Some OSes can produce a route that has the LINK flag but + * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD + * and BSD/OS, where xx is not the interface identifier on + * lo0). Such routes entry would annoy getnbrinfo() below, + * so we skip them. + * XXX: such routes should have the GATEWAY flag, not the + * LINK flag. However, there are rotten routing software + * that advertises all routes that have the GATEWAY flag. + * Thus, KAME kernel intentionally does not set the LINK flag. + * What is to be fixed is not ndp, but such routing software + * (and the kernel workaround)... + */ + if (sdl->sdl_family != AF_LINK) + continue; + + if (addr) { + if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr)) + continue; + found_entry = 1; + } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) + continue; + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&sin->sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { + /* should scope id be filled in the kernel? */ + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = sdl->sdl_index; +#ifdef __KAME__ + /* KAME specific hack; removed the embedded id */ + *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0; +#endif + } + getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, + sizeof (host_buf), NULL, 0, + NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0)); + if (cflag == 1) { + if (ertm->rtm_flags & RTF_WASCLONED) + delete(host_buf); + continue; + } + gettimeofday(&time, 0); + if (tflag) + ts_print(&time); + + addrwidth = strlen(host_buf); + if (addrwidth < W_ADDR) + addrwidth = W_ADDR; + llwidth = strlen(ether_str(sdl)); + if (W_ADDR + W_LL - addrwidth > llwidth) + llwidth = W_ADDR + W_LL - addrwidth; + ifname = if_indextoname(sdl->sdl_index, ifix_buf); + if (!ifname) + ifname = "?"; + ifwidth = strlen(ifname); + if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) + ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; + + printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, + llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); + + if (ertm->rtm_ri.ri_refcnt == 0 || + ertm->rtm_ri.ri_snd_expire == 0) + printf(" %-9.9s", "(none)"); + else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec) + printf(" %-9.9s", + sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec)); + else + printf(" %-9.9s", "expired"); + + if (ertm->rtm_ri.ri_refcnt == 0 || + ertm->rtm_ri.ri_rcv_expire == 0) + printf(" %-9.9s", "(none)"); + else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec) + printf(" %-9.9s", + sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec)); + else + printf(" %-9.9s", "expired"); + + /* Print neighbor discovery specific informations */ + nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1); + if (nbi) { + switch (nbi->state) { + case ND6_LLINFO_NOSTATE: + printf(" N"); + break; + case ND6_LLINFO_INCOMPLETE: + printf(" I"); + break; + case ND6_LLINFO_REACHABLE: + printf(" R"); + break; + case ND6_LLINFO_STALE: + printf(" S"); + break; + case ND6_LLINFO_DELAY: + printf(" D"); + break; + case ND6_LLINFO_PROBE: + printf(" P"); + break; + default: + printf(" ?"); + break; + } + + isrouter = nbi->isrouter; + prbs = nbi->asked; + } else { + warnx("failed to get neighbor information"); + printf(" "); + } + putchar(' '); + + /* + * other flags. R: router, P: proxy, W: ?? + */ + if ((ertm->rtm_addrs & RTA_NETMASK) == 0) { + snprintf(flgbuf, sizeof (flgbuf), "%s%s", + isrouter ? "R" : "", + (ertm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); + } else { + sin = (struct sockaddr_in6 *) + (sdl->sdl_len + (char *)sdl); + snprintf(flgbuf, sizeof (flgbuf), "%s%s%s%s", + isrouter ? "R" : "", + !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) + ? "P" : "", + (sin->sin6_len != sizeof (struct sockaddr_in6)) + ? "W" : "", + (ertm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); + } + printf(" %-4.4s", flgbuf); + + if (prbs) + printf(" %4d", prbs); + + if (xflag) { + if (!prbs) + printf(" %-4.4s", "none"); + + if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN) + printf(" %7d", ertm->rtm_ri.ri_rssi); + else + printf(" %-7.7s", "unknown"); + + switch (ertm->rtm_ri.ri_lqm) + { + case IFNET_LQM_THRESH_OFF: + printf(" %-7.7s", "off"); + break; + case IFNET_LQM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_LQM_THRESH_POOR: + printf(" %-7.7s", "poor"); + break; + case IFNET_LQM_THRESH_GOOD: + printf(" %-7.7s", "good"); + break; + default: + printf(" %7d", ertm->rtm_ri.ri_lqm); + break; + } + + switch (ertm->rtm_ri.ri_npm) + { + case IFNET_NPM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_NPM_THRESH_NEAR: + printf(" %-7.7s", "near"); + break; + case IFNET_NPM_THRESH_GENERAL: + printf(" %-7.7s", "general"); + break; + case IFNET_NPM_THRESH_FAR: + printf(" %-7.7s", "far"); + break; + default: + printf(" %7d", ertm->rtm_ri.ri_npm); + break; + } + } + + printf("\n"); + } + if (buf != NULL) + free(buf); + + if (repeat) { + printf("\n"); + sleep(repeat); + goto again; + } +} + +static struct in6_nbrinfo * +getnbrinfo(addr, ifindex, warning) + struct in6_addr *addr; + int ifindex; + int warning; +{ + static struct in6_nbrinfo nbi; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + + bzero(&nbi, sizeof (nbi)); + if_indextoname(ifindex, nbi.ifname); + nbi.addr = *addr; + if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) { + if (warning) + warn("ioctl(SIOCGNBRINFO_IN6)"); + close(s); + return (NULL); + } + + close(s); + return (&nbi); +} + +static char * +ether_str(struct sockaddr_dl *sdl) +{ + static char ebuf[32]; + u_char *cp; + + if (sdl->sdl_alen) { + cp = (u_char *)LLADDR(sdl); + snprintf(ebuf, sizeof (ebuf), "%x:%x:%x:%x:%x:%x", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); + } else { + snprintf(ebuf, sizeof (ebuf), "(incomplete)"); + } + + return (ebuf); +} + +static int +ndp_ether_aton(char *a, u_char *n) +{ + int i, o[8]; + int len; + + len = sscanf(a, "%x:%x:%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], &o[3], &o[4], + &o[5], &o[6], &o[7]); + if (len != 6 && len != 8) { + fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a); + return (-1); + } + for (i = 0; i < len; i++) + n[i] = o[i]; + return (len); +} + +static void +usage(void) +{ + printf("usage: ndp hostname\n"); + printf(" ndp -a[lnt]\n"); + printf(" ndp [-nt] -A wait\n"); + printf(" ndp -c[nt]\n"); + printf(" ndp -d[nt] hostname\n"); + printf(" ndp -f[nt] filename\n"); + printf(" ndp -i interface [flags...]\n"); + printf(" ndp -I [interface|delete]\n"); + printf(" ndp -p\n"); + printf(" ndp -r\n"); + printf(" ndp -s hostname ether_addr [temp] [proxy]\n"); + printf(" ndp -H\n"); + printf(" ndp -P\n"); + printf(" ndp -R\n"); + printf(" ndp -w\n"); + printf(" ndp -W cfgfile\n"); + exit(1); +} + +static int +rtmsg(int cmd) +{ + static int seq; + int rlen; + register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + register char *cp = m_rtmsg.m_space; + register int l; + + errno = 0; + if (cmd == RTM_DELETE) + goto doit; + bzero((char *)&m_rtmsg, sizeof (m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + switch (cmd) { + default: + fprintf(stderr, "ndp: internal wrong cmd\n"); + exit(1); + case RTM_ADD: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); + if (rtm->rtm_flags & RTF_ANNOUNCE) { + rtm->rtm_flags &= ~RTF_HOST; + rtm->rtm_flags |= RTA_NETMASK; + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if (rtm->rtm_addrs & (w)) { \ + bcopy((char *)&s, cp, sizeof (s)); cp += sizeof (s); \ + } + + NEXTADDR(RTA_DST, sin_m); + NEXTADDR(RTA_GATEWAY, sdl_m); + memset(&so_mask.sin6_addr, 0xff, sizeof (so_mask.sin6_addr)); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; +doit: + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if (errno != ESRCH || cmd != RTM_DELETE) { + perror("writing to routing socket"); + return (-1); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg)); + } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); + if (l < 0) + (void) fprintf(stderr, "ndp: read from routing socket: %s\n", + strerror(errno)); + return (0); +} + +static void +ifinfo(int argc, char **argv) +{ + struct in6_ndireq nd; + int i, s; + char *ifname = argv[0]; + u_int32_t newflags; + u_int8_t nullbuf[8]; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("ndp: socket"); + exit(1); + } + bzero(&nd, sizeof (nd)); + strlcpy(nd.ifname, ifname, sizeof (nd.ifname)); + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { + perror("ioctl (SIOCGIFINFO_IN6)"); + exit(1); + } +#define ND nd.ndi + newflags = ND.flags; + for (i = 1; i < argc; i++) { + int clear = 0; + char *cp = argv[i]; + + if (*cp == '-') { + clear = 1; + cp++; + } + +#define SETFLAG(s, f) \ + do {\ + if (strcmp(cp, (s)) == 0) {\ + if (clear)\ + newflags &= ~(f);\ + else\ + newflags |= (f);\ + }\ + } while (0) + SETFLAG("nud", ND6_IFF_PERFORMNUD); + SETFLAG("proxy_prefixes", ND6_IFF_PROXY_PREFIXES); + SETFLAG("disabled", ND6_IFF_IFDISABLED); + SETFLAG("insecure", ND6_IFF_INSECURE); + SETFLAG("replicated", ND6_IFF_REPLICATED); + + ND.flags = newflags; + if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) { + perror("ioctl(SIOCSIFINFO_FLAGS)"); + exit(1); + } +#undef SETFLAG + } + + printf("linkmtu=%d", ND.linkmtu); + printf(", curhlim=%d", ND.chlim); + printf(", basereachable=%ds%dms", ND.basereachable / 1000, + ND.basereachable % 1000); + printf(", reachable=%ds", ND.reachable); + printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000); + memset(nullbuf, 0, sizeof (nullbuf)); + if (memcmp(nullbuf, ND.randomid, sizeof (nullbuf)) != 0) { + int j; + u_int8_t *rbuf = NULL; + + for (i = 0; i < 3; i++) { + switch (i) { + case 0: + printf("\nRandom seed(0): "); + rbuf = ND.randomseed0; + break; + case 1: + printf("\nRandom seed(1): "); + rbuf = ND.randomseed1; + break; + case 2: + printf("\nRandom ID: "); + rbuf = ND.randomid; + break; + } + for (j = 0; j < 8; j++) + printf("%02x", rbuf[j]); + } + } + if (ND.flags) { + printf("\nFlags: 0x%x ", ND.flags); + if ((ND.flags & ND6_IFF_IFDISABLED) != 0) + printf("IFDISABLED "); + if ((ND.flags & ND6_IFF_INSECURE) != 0) + printf("INSECURE "); + if ((ND.flags & ND6_IFF_PERFORMNUD) != 0) + printf("PERFORMNUD "); + if ((ND.flags & ND6_IFF_PROXY_PREFIXES) != 0) + printf("PROXY_PREFIXES "); + if ((ND.flags & ND6_IFF_REPLICATED) != 0) + printf("REPLICATED "); + if ((ND.flags & ND6_IFF_DAD) != 0) + printf("DAD "); + } + putc('\n', stdout); +#undef ND + + close(s); +} + +#ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */ +#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ +#endif + +static void +rtrlist(void) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST }; + char *buf; + struct in6_defrouter *p, *ep; + size_t l; + struct timeval time; + + if (sysctl(mib, sizeof (mib) / sizeof (mib[0]), NULL, &l, NULL, 0) + < 0) { + err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); + /*NOTREACHED*/ + } + buf = malloc(l); + if (!buf) { + errx(1, "not enough core"); + /*NOTREACHED*/ + } + if (sysctl(mib, sizeof (mib) / sizeof (mib[0]), buf, &l, NULL, 0) < 0) { + err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); + /*NOTREACHED*/ + } + + ep = (struct in6_defrouter *)(buf + l); + for (p = (struct in6_defrouter *)buf; p < ep; p++) { + int rtpref; + + if (getnameinfo((struct sockaddr *)&p->rtaddr, + p->rtaddr.sin6_len, host_buf, sizeof (host_buf), NULL, 0, + NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0)) != 0) + strlcpy(host_buf, "?", sizeof (host_buf)); + + printf("%s if=%s", host_buf, if_indextoname(p->if_index, + ifix_buf)); + printf(", flags=%s%s%s%s%s", + p->stateflags & NDDRF_IFSCOPE ? "I" : "", + p->flags & ND_RA_FLAG_MANAGED ? "M" : "", + p->flags & ND_RA_FLAG_OTHER ? "O" : "", + p->stateflags & NDDRF_STATIC ? "S" : "", + p->stateflags & NDDRF_INSTALLED ? "T" : ""); + rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff; + printf(", pref=%s", rtpref_str[rtpref]); + + gettimeofday(&time, 0); + if (p->expire == 0) + printf(", expire=Never\n"); + else + printf(", expire=%s\n", + sec2str(p->expire - time.tv_sec)); + } + free(buf); +} + +static void +plist(void) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST }; + char *buf; + struct in6_prefix *p, *ep, *n; + struct sockaddr_in6 *advrtr; + size_t l; + struct timeval time; + const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; + int ninflags = (nflag ? NI_NUMERICHOST : 0) | NI_WITHSCOPEID; + char namebuf[NI_MAXHOST]; + + if (sysctl(mib, sizeof (mib) / sizeof (mib[0]), NULL, &l, NULL, 0) + < 0) { + err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); + /*NOTREACHED*/ + } + buf = malloc(l); + if (!buf) { + errx(1, "not enough core"); + /*NOTREACHED*/ + } + if (sysctl(mib, sizeof (mib) / sizeof (mib[0]), buf, &l, NULL, 0) + < 0) { + err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); + /*NOTREACHED*/ + } + + ep = (struct in6_prefix *)(buf + l); + for (p = (struct in6_prefix *)buf; p < ep; p = n) { + advrtr = (struct sockaddr_in6 *)(p + 1); + n = (struct in6_prefix *)&advrtr[p->advrtrs]; + + if (getnameinfo((struct sockaddr *)&p->prefix, + p->prefix.sin6_len, namebuf, sizeof (namebuf), + NULL, 0, niflags) != 0) + strlcpy(namebuf, "?", sizeof (namebuf)); + printf("%s/%d if=%s\n", namebuf, p->prefixlen, + if_indextoname(p->if_index, ifix_buf)); + + gettimeofday(&time, 0); + /* + * meaning of fields, especially flags, is very different + * by origin. notify the difference to the users. + */ + printf("flags=%s%s%s%s%s%s%s", + p->raflags.autonomous ? "A" : "", + (p->flags & NDPRF_DETACHED) != 0 ? "D" : "", + (p->flags & NDPRF_IFSCOPE) != 0 ? "I" : "", + p->raflags.onlink ? "L" : "", + (p->flags & NDPRF_STATIC) != 0 ? "S" : "", + (p->flags & NDPRF_ONLINK) != 0 ? "O" : "", + (p->flags & NDPRF_PRPROXY) != 0 ? "Y" : ""); + if (p->vltime == ND6_INFINITE_LIFETIME) + printf(" vltime=infinity"); + else + printf(" vltime=%ld", (long)p->vltime); + if (p->pltime == ND6_INFINITE_LIFETIME) + printf(", pltime=infinity"); + else + printf(", pltime=%ld", (long)p->pltime); + if (p->expire == 0) + printf(", expire=Never"); + else if (p->expire >= time.tv_sec) + printf(", expire=%s", + sec2str(p->expire - time.tv_sec)); + else + printf(", expired"); + printf(", ref=%d", p->refcnt); + printf("\n"); + /* + * "advertising router" list is meaningful only if the prefix + * information is from RA. + */ + if (p->advrtrs) { + int j; + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)(p + 1); + printf(" advertised by\n"); + for (j = 0; j < p->advrtrs; j++) { + struct in6_nbrinfo *nbi; + + if (getnameinfo((struct sockaddr *)sin6, + sin6->sin6_len, namebuf, sizeof (namebuf), + NULL, 0, ninflags) != 0) + strlcpy(namebuf, "?", sizeof (namebuf)); + printf(" %s", namebuf); + + nbi = getnbrinfo(&sin6->sin6_addr, p->if_index, + 0); + if (nbi) { + switch (nbi->state) { + case ND6_LLINFO_REACHABLE: + case ND6_LLINFO_STALE: + case ND6_LLINFO_DELAY: + case ND6_LLINFO_PROBE: + printf(" (reachable)\n"); + break; + default: + printf(" (unreachable)\n"); + } + } else + printf(" (no neighbor state)\n"); + sin6++; + } + } else + printf(" No advertising router\n"); + } + free(buf); +} + +static void +pfx_flush(void) +{ + char dummyif[IFNAMSIZ+8]; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + strlcpy(dummyif, "lo0", sizeof (dummyif)); /* dummy */ + if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0) + err(1, "ioctl(SIOCSPFXFLUSH_IN6)"); +} + +static void +rtr_flush(void) +{ + char dummyif[IFNAMSIZ+8]; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + strlcpy(dummyif, "lo0", sizeof (dummyif)); /* dummy */ + if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0) + err(1, "ioctl(SIOCSRTRFLUSH_IN6)"); + + close(s); +} + +static void +harmonize_rtr(void) +{ + char dummyif[IFNAMSIZ+8]; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + strlcpy(dummyif, "lo0", sizeof (dummyif)); /* dummy */ + if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) + err(1, "ioctl (SIOCSNDFLUSH_IN6)"); + + close(s); +} + +static void +setdefif(char *ifname) +{ + struct in6_ndifreq ndifreq; + unsigned int ifindex; + + if (strcasecmp(ifname, "delete") == 0) + ifindex = 0; + else { + if ((ifindex = if_nametoindex(ifname)) == 0) + err(1, "failed to resolve i/f index for %s", ifname); + } + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + + strlcpy(ndifreq.ifname, "lo0", sizeof (ndifreq.ifname)); /* dummy */ + ndifreq.ifindex = ifindex; + + if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) + err(1, "ioctl (SIOCSDEFIFACE_IN6)"); + + close(s); +} + +static void +getdefif(void) +{ + struct in6_ndifreq ndifreq; + char ifname[IFNAMSIZ+8]; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + + memset(&ndifreq, 0, sizeof (ndifreq)); + strlcpy(ndifreq.ifname, "lo0", sizeof (ndifreq.ifname)); /* dummy */ + + if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) + err(1, "ioctl (SIOCGDEFIFACE_IN6)"); + + if (ndifreq.ifindex == 0) + printf("No default interface.\n"); + else { + if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL) + err(1, "failed to resolve ifname for index %lu", + ndifreq.ifindex); + printf("ND default interface = %s\n", ifname); + } + + close(s); +} + +static char * +sec2str(time_t total) +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof (result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof (result) - (p - result), "%dh", hours); + } + if (!first || mins) + p += snprintf(p, sizeof (result) - (p - result), "%dm", mins); + snprintf(p, sizeof (result) - (p - result), "%ds", secs); + + return (result); +} + +/* + * Print the timestamp + * from tcpdump/util.c + */ +static void +ts_print(const struct timeval *tvp) +{ + int s; + + /* Default */ + s = (tvp->tv_sec + thiszone) % 86400; + printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, + (u_int32_t)tvp->tv_usec); +} + +#define SYSCTL_CGA_PARAMETERS_BUFFER_SIZE \ + 2 * (sizeof (size_t) + IN6_CGA_KEY_MAXSIZE) + \ + sizeof (struct in6_cga_prepare) + +static void +read_cga_parameters(void) +{ + static char oldb[SYSCTL_CGA_PARAMETERS_BUFFER_SIZE]; + + int error; + struct in6_cga_nodecfg cfg; + struct iovec *iov; + const char *oldp; + const char *finp; + size_t oldn; + unsigned int column; + uint16_t u16; + + oldn = sizeof oldb; + error = sysctlbyname("net.inet6.send.cga_parameters", oldb, &oldn, + NULL, NULL); + if (error != 0) + err(1, "sysctlbyname"); + + if (oldn == 0) { + printf("No CGA parameters.\n"); + exit(0); + } + + oldp = oldb; + finp = &oldb[oldn]; + memset(&cfg, 0, sizeof (cfg)); + + if (oldp + sizeof (cfg.cga_prepare) > finp) + err(1, "format error[1]"); + + memcpy(&cfg.cga_prepare, oldp, sizeof (cfg.cga_prepare)); + oldp += sizeof (cfg.cga_prepare); + + iov = &cfg.cga_pubkey; + + if (oldp + sizeof (u16) > finp) + err(1, "format error[2]"); + + memcpy(&u16, oldp, sizeof (u16)); + oldp += sizeof (u16); + iov->iov_len = u16; + + if (oldp + iov->iov_len > finp) + err(1, "format error[3]"); + + iov->iov_base = (void *)oldp; + oldp += iov->iov_len; + + if (oldp != finp) + err(1, "format error[4]"); + + puts("Public Key:"); + finp = &iov->iov_base[iov->iov_len]; + column = 0; + oldp = iov->iov_base; + while (oldp < finp) { + if (column++ != 0) + putchar(':'); + printf("%02x", (unsigned char) *oldp++); + if (column >= 32) { + column = 0; + puts(""); + } + } + if (column < 32) + puts(""); + puts(""); + puts("Modifier:"); + oldp = (const char*) cfg.cga_prepare.cga_modifier.octets; + finp = &oldp[sizeof (cfg.cga_prepare.cga_modifier.octets)]; + column = 0; + while (oldp < finp) { + if (column++ != 0) + putchar(':'); + printf("%02x", (unsigned char) *oldp++); + } + puts("\n"); + printf("Security Level: %u\n", cfg.cga_prepare.cga_security_level); +} + +static void +write_cga_parameters(const char filename[]) +{ + static char newb[SYSCTL_CGA_PARAMETERS_BUFFER_SIZE]; + + int error; + FILE* fp; + size_t oldn, newn; + + fp = fopen(filename, "r"); + if (fp == NULL) + err(1, "opening '%s' for reading.", filename); + + newn = fread(newb, 1, sizeof (newb), fp); + if (feof(fp) == 0) + err(1, "parameters too large"); + + if (fclose(fp) != 0) + err(1, "closing file."); + + oldn = 0; + error = sysctlbyname("net.inet6.send.cga_parameters", NULL, NULL, newb, + newn); + if (error != 0) + err(1, "sysctlbyname"); +} diff --git a/network_cmds/netstat.tproj/DERIVED_FILES b/network_cmds/netstat.tproj/DERIVED_FILES new file mode 100644 index 0000000..a4fa6c9 --- /dev/null +++ b/network_cmds/netstat.tproj/DERIVED_FILES @@ -0,0 +1 @@ +unix/bsd/netiso/tp_astring.c diff --git a/network_cmds/netstat.tproj/data.c b/network_cmds/netstat.tproj/data.c new file mode 100644 index 0000000..9e0bbe2 --- /dev/null +++ b/network_cmds/netstat.tproj/data.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* NeXT */ +#ifndef EXTERN +#define EXTERN +#endif +#include "netstat.h" diff --git a/network_cmds/netstat.tproj/if.c b/network_cmds/netstat.tproj/if.c new file mode 100644 index 0000000..2fe6182 --- /dev/null +++ b/network_cmds/netstat.tproj/if.c @@ -0,0 +1,2259 @@ +/* + * Copyright (c) 2008-2019 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/kern_control.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_mib.h> +#include <net/if_llreach.h> +#include <net/ethernet.h> +#include <net/route.h> +#include <net/ntstat.h> + +#include <net/pktsched/pktsched.h> +#include <net/classq/if_classq.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> + +#include <arpa/inet.h> + +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stddef.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> + +#include <assert.h> + +#include "netstat.h" + +#define YES 1 +#define NO 0 + +#define ROUNDUP(a, size) (((a) & ((size) - 1)) ? (1 + ((a)|(size - 1))) : (a)) + +#define NEXT_SA(p) (struct sockaddr *) \ + ((caddr_t)p + (p->sa_len ? ROUNDUP(p->sa_len, sizeof(uint32_t)) : \ + sizeof(uint32_t))) + +static void sidewaysintpr (); +static void catchalarm (int); +static char *sec2str(time_t); +static void llreach_sysctl(uint32_t); +static char *nsec_to_str(unsigned long long); +static char *sched2str(unsigned int); +static char *pri2str(unsigned int i); + +#define AVGN_MAX 8 + +struct queue_stats { + int avgn; + double avg_bytes; + double avg_packets; + u_int64_t prev_bytes; + u_int64_t prev_packets; + unsigned int handle; +}; + +static void update_avg(struct if_ifclassq_stats *, struct queue_stats *); +static void print_fq_codel_stats(int slot, struct fq_codel_classstats *, + struct queue_stats *); + +struct queue_stats qstats[IFCQ_SC_MAX]; + +#ifdef INET6 +char *netname6 (struct sockaddr_in6 *, struct sockaddr *); +static char ntop_buf[INET6_ADDRSTRLEN]; /* for inet_ntop() */ +#endif + +/* + * Display a formatted value, or a '-' in the same space. + */ +static void +show_stat(const char *fmt, int width, u_int64_t value, short showvalue) +{ + char newfmt[32]; + + /* Construct the format string */ + if (showvalue) { + snprintf(newfmt, sizeof(newfmt), "%%%d%s", width, fmt); + printf(newfmt, value); + } else { + snprintf(newfmt, sizeof(newfmt), "%%%ds", width); + printf(newfmt, "-"); + } +} + +size_t +get_rti_info(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + size_t len = 0; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + if (sa->sa_len < sizeof(struct sockaddr)) + len += sizeof(struct sockaddr); + else + len += sa->sa_len; + sa = NEXT_SA(sa); + } else { + rti_info[i] = NULL; + } + } + return len; +} + +static void +multipr(int family, char *buf, char *lim) +{ + char *next; + + for (next = buf; next < lim; ) { + struct ifma_msghdr2 *ifmam = (struct ifma_msghdr2 *)next; + struct sockaddr *rti_info[RTAX_MAX]; + struct sockaddr *sa; + const char *fmt = 0; + + next += ifmam->ifmam_msglen; + if (ifmam->ifmam_type == RTM_IFINFO2) + break; + else if (ifmam->ifmam_type != RTM_NEWMADDR2) + continue; + get_rti_info(ifmam->ifmam_addrs, (struct sockaddr*)(ifmam + 1), rti_info); + sa = rti_info[RTAX_IFA]; + + if (sa->sa_family != family) + continue; + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + fmt = routename(sin->sin_addr.s_addr); + break; + } + #ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 sin6; + + memcpy(&sin6, sa, sizeof(struct sockaddr_in6)); + + if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) { + sin6.sin6_scope_id = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } + + printf("%23s %-19.19s(refs: %d)\n", "", + inet_ntop(AF_INET6, &sin6.sin6_addr, + ntop_buf, sizeof(ntop_buf)), + ifmam->ifmam_refcount); + break; + } + #endif /* INET6 */ + case AF_LINK: { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + + switch (sdl->sdl_type) { + case IFT_ETHER: + case IFT_FDDI: + fmt = ether_ntoa((struct ether_addr *) + LLADDR(sdl)); + break; + } + break; + } + } + if (fmt) + printf("%23s %s\n", "", fmt); + } +} + +/* + * Print a description of the network interfaces. + */ +void +intpr(void (*pfunc)(char *)) +{ + u_int64_t opackets = 0; + u_int64_t ipackets = 0; + u_int64_t obytes = 0; + u_int64_t ibytes = 0; + u_int64_t oerrors = 0; + u_int64_t ierrors = 0; + u_int64_t collisions = 0; + u_int64_t fpackets = 0; + u_int64_t fbytes = 0; + uint32_t mtu = 0; + int timer = 0; + int drops = 0; + struct sockaddr *sa = NULL; + char name[32]; + short network_layer; + short link_layer; + int mib[6]; + char *buf = NULL, *lim, *next; + size_t len; + struct if_msghdr *ifm; + struct sockaddr *rti_info[RTAX_MAX]; + unsigned int ifindex = 0; + + if (interval) { + sidewaysintpr(); + return; + } + + if (interface != 0) + ifindex = if_nametoindex(interface); + + mib[0] = CTL_NET; // networking subsystem + mib[1] = PF_ROUTE; // type of information + mib[2] = 0; // protocol (IPPROTO_xxx) + mib[3] = 0; // address family + mib[4] = NET_RT_IFLIST2; // operation + mib[5] = 0; + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return; + if ((buf = malloc(len)) == NULL) { + printf("malloc failed\n"); + exit(1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + if (buf) + free(buf); + return; + } + + if (!pfunc) { + if (lflag) { + printf("%-10.10s %-5.5s %-39.39s %-39.39s %8.8s %5.5s", + "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs"); + } else { + printf("%-10.10s %-5.5s %-13.13s %-15.15s %8.8s %5.5s", + "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs"); + } + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Itcpkts", "Ipvpkts"); + if (bflag) { + printf(" %10.10s","Ibytes"); + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Itcbytes", "Ipvbytes"); + } + printf(" %8.8s %5.5s", "Opkts", "Oerrs"); + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Otcpkts", "Opvpkts"); + if (bflag) { + printf(" %10.10s","Obytes"); + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Otcbytes", "Opvbytes"); + } + printf(" %5s", "Coll"); + if (tflag) + printf(" %s", "Time"); + if (dflag) + printf(" %s", "Drop"); + if (Fflag) { + printf(" %8.8s", "Fpkts"); + if (bflag) + printf(" %10.10s", "Fbytes"); + } + putchar('\n'); + } + lim = buf + len; + for (next = buf; next < lim; ) { + char *cp; + int n, m; + struct ifmibdata_supplemental ifmsupp; + u_int64_t ift_itcp = 0; /* input tc packets */ + u_int64_t ift_itcb = 0; /* input tc bytes */ + u_int64_t ift_otcp = 0; /* output tc packets */ + u_int64_t ift_otcb = 0; /* output tc bytes */ + u_int64_t ift_ipvp = 0; /* input priv tc packets */ + u_int64_t ift_ipvb = 0; /* input priv tc bytes */ + u_int64_t ift_opvp = 0; /* output priv tc packets */ + u_int64_t ift_opvb = 0; /* output priv tc bytes */ + + bzero(&ifmsupp, sizeof(struct ifmibdata_supplemental)); + + network_layer = 0; + link_layer = 0; + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + + if (ifm->ifm_type == RTM_IFINFO2) { + struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm; + struct sockaddr_dl *sdl = + (struct sockaddr_dl *)(if2m + 1); + int mibname[6]; + size_t miblen = sizeof(struct ifmibdata_supplemental); + + if (interface != 0 && if2m->ifm_index != ifindex) + continue; + + /* The interface name is not a zero-ended string */ + memcpy(name, sdl->sdl_data, MIN(sizeof(name) - 1, sdl->sdl_nlen)); + name[MIN(sizeof(name) - 1, sdl->sdl_nlen)] = 0; + + if (pfunc) { + (*pfunc)(name); + continue; + } + + cp = index(name, '\0'); + if ((if2m->ifm_flags & IFF_UP) == 0) + *cp++ = '*'; + *cp = '\0'; + + /* + * Get the interface stats. These may get + * overriden below on a per-interface basis. + */ + opackets = if2m->ifm_data.ifi_opackets; + ipackets = if2m->ifm_data.ifi_ipackets; + obytes = if2m->ifm_data.ifi_obytes; + ibytes = if2m->ifm_data.ifi_ibytes; + oerrors =if2m->ifm_data.ifi_oerrors; + ierrors = if2m->ifm_data.ifi_ierrors; + collisions = if2m->ifm_data.ifi_collisions; + timer = if2m->ifm_timer; + drops = if2m->ifm_snd_drops; + mtu = if2m->ifm_data.ifi_mtu; + + /* Common OID prefix */ + mibname[0] = CTL_NET; + mibname[1] = PF_LINK; + mibname[2] = NETLINK_GENERIC; + mibname[3] = IFMIB_IFDATA; + mibname[4] = if2m->ifm_index; + mibname[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(mibname, 6, &ifmsupp, &miblen, NULL, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL"); + + fpackets = ifmsupp.ifmd_data_extended.ifi_fpackets; + fbytes = ifmsupp.ifmd_data_extended.ifi_fbytes; + + if (prioflag >= 0) { + switch (prioflag) { + case SO_TC_BE: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ibepackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ibebytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_obepackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_obebytes; + break; + case SO_TC_BK: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ibkpackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ibkbytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_obkpackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_obkbytes; + break; + case SO_TC_VI: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ivipackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ivibytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_ovipackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_ovibytes; + break; + case SO_TC_VO: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ivopackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ivobytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_ovopackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_ovobytes; + break; + default: + ift_itcp = 0; + ift_itcb = 0; + ift_otcp = 0; + ift_otcb = 0; + ift_ipvp = 0; + ift_ipvb = 0; + ift_opvp = 0; + ift_opvb = 0; + break; + } + ift_ipvp = ifmsupp.ifmd_traffic_class.ifi_ipvpackets; + ift_ipvb = ifmsupp.ifmd_traffic_class.ifi_ipvbytes; + ift_opvp = ifmsupp.ifmd_traffic_class.ifi_opvpackets; + ift_opvb = ifmsupp.ifmd_traffic_class.ifi_opvbytes; + } + + get_rti_info(if2m->ifm_addrs, + (struct sockaddr*)(if2m + 1), rti_info); + sa = rti_info[RTAX_IFP]; + } else if (ifm->ifm_type == RTM_NEWADDR) { + struct ifa_msghdr *ifam = (struct ifa_msghdr *)ifm; + + if (interface != 0 && ifam->ifam_index != ifindex) + continue; + get_rti_info(ifam->ifam_addrs, + (struct sockaddr*)(ifam + 1), rti_info); + sa = rti_info[RTAX_IFA]; + } else { + continue; + } + if (lflag) { + printf("%-10.10s %-5u ", name, mtu); + } else { + printf("%-5.5s %-5u ", name, mtu); + } + + if (sa == 0) { + printf(lflag ? "%-39.39s " : "%-13.13s ", "none"); + printf(lflag ? "%-39.39s " : "%-15.15s ", "none"); + } else { + switch (sa->sa_family) { + case AF_UNSPEC: + printf(lflag ? "%-39.39s " : "%-13.13s ", "none"); + printf(lflag ? "%-39.39s " : "%-15.15s ", "none"); + break; + + case AF_INET: { + struct sockaddr_in *sin = + (struct sockaddr_in *)sa; + struct sockaddr_in mask; + + mask.sin_addr.s_addr = 0; + memcpy(&mask, rti_info[RTAX_NETMASK], + ((struct sockaddr_in *) + rti_info[RTAX_NETMASK])->sin_len); + + printf(lflag ? "%-39.39s " : "%-13.13s ", + netname(sin->sin_addr.s_addr & + mask.sin_addr.s_addr, + ntohl(mask.sin_addr.s_addr))); + + printf(lflag ? "%-39.39s " : "%-15.15s ", + routename(sin->sin_addr.s_addr)); + + network_layer = 1; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = + (struct sockaddr_in6 *)sa; + struct sockaddr *mask = + (struct sockaddr *)rti_info[RTAX_NETMASK]; + + printf(lflag ? "%-39.39s " : "%-11.11s ", netname6(sin6, mask)); + printf(lflag ? "%-39.39s " : "%-17.17s ", (char *)inet_ntop(AF_INET6, + &sin6->sin6_addr, ntop_buf, + sizeof(ntop_buf))); + + network_layer = 1; + break; + } +#endif /*INET6*/ + case AF_LINK: { + struct sockaddr_dl *sdl = + (struct sockaddr_dl *)sa; + char linknum[10]; + cp = (char *)LLADDR(sdl); + n = sdl->sdl_alen; + snprintf(linknum, sizeof(linknum), + "<Link#%d>", sdl->sdl_index); + m = printf(lflag ? "%-39.39s " : "%-11.11s ", linknum); + goto hexprint; + } + + default: + m = printf("(%d)", sa->sa_family); + for (cp = sa->sa_len + (char *)sa; + --cp > sa->sa_data && (*cp == 0);) {} + n = cp - sa->sa_data + 1; + cp = sa->sa_data; + hexprint: + while (--n >= 0) + m += printf("%02x%c", *cp++ & 0xff, + n > 0 ? ':' : ' '); + m = (lflag ? 80 : 30) - m; + while (m-- > 0) + putchar(' '); + + link_layer = 1; + break; + } + } + + show_stat("llu", 8, ipackets, link_layer|network_layer); + printf(" "); + show_stat("llu", 5, ierrors, link_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_itcp, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_ipvp, link_layer|network_layer); + printf(" "); + } + if (bflag) { + show_stat("llu", 10, ibytes, link_layer|network_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_itcb, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_ipvb, link_layer|network_layer); + printf(" "); + } + } + show_stat("llu", 8, opackets, link_layer|network_layer); + printf(" "); + show_stat("llu", 5, oerrors, link_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_otcp, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_opvp, link_layer|network_layer); + printf(" "); + } + if (bflag) { + show_stat("llu", 10, obytes, link_layer|network_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_otcb, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_opvb, link_layer|network_layer); + printf(" "); + } + } + show_stat("llu", 5, collisions, link_layer); + if (tflag) { + printf(" "); + show_stat("d", 3, timer, link_layer); + } + if (dflag) { + printf(" "); + show_stat("d", 3, drops, link_layer); + } + if (Fflag) { + printf(" "); + show_stat("llu", 8, fpackets, link_layer|network_layer); + if (bflag) { + printf(" "); + show_stat("llu", 10, fbytes, + link_layer|network_layer); + } + } + putchar('\n'); + + if (aflag) + multipr(sa->sa_family, next, lim); + } + free(buf); +} + +struct iftot { + SLIST_ENTRY(iftot) chain; + char ift_name[16]; /* interface name */ + u_int64_t ift_ip; /* input packets */ + u_int64_t ift_ie; /* input errors */ + u_int64_t ift_op; /* output packets */ + u_int64_t ift_oe; /* output errors */ + u_int64_t ift_co; /* collisions */ + u_int64_t ift_dr; /* drops */ + u_int64_t ift_ib; /* input bytes */ + u_int64_t ift_ob; /* output bytes */ + u_int64_t ift_itcp; /* input tc packets */ + u_int64_t ift_itcb; /* input tc bytes */ + u_int64_t ift_otcp; /* output tc packets */ + u_int64_t ift_otcb; /* output tc bytes */ + u_int64_t ift_ipvp; /* input priv tc packets */ + u_int64_t ift_ipvb; /* input priv tc bytes */ + u_int64_t ift_opvp; /* output priv tc packets */ + u_int64_t ift_opvb; /* output priv tc bytes */ + u_int64_t ift_fp; /* forwarded packets */ + u_int64_t ift_fb; /* forwarded bytes */ +}; + +u_char signalled; /* set if alarm goes off "early" */ + +/* + * Print a running summary of interface statistics. + * Repeat display every interval seconds, showing statistics + * collected over that interval. Assumes that interval is non-zero. + * First line printed at top of screen is always cumulative. + * XXX - should be rewritten to use ifmib(4). + */ +static void +sidewaysintpr() +{ + struct iftot *total, *sum, *interesting; + register int line; + int first; + int name[6]; + size_t len; + unsigned int ifcount, i; + struct ifmibdata *ifmdall = 0; + int interesting_row; + sigset_t sigset, oldsigset; + struct itimerval timer_interval; + + /* Common OID prefix */ + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + + len = sizeof(int); + name[3] = IFMIB_SYSTEM; + name[4] = IFMIB_IFCOUNT; + if (sysctl(name, 5, &ifcount, &len, 0, 0) == 1) + err(1, "sysctl IFMIB_IFCOUNT"); + + len = ifcount * sizeof(struct ifmibdata); + ifmdall = malloc(len); + if (ifmdall == 0) + err(1, "malloc failed"); + name[3] = IFMIB_IFALLDATA; + name[4] = 0; + name[5] = IFDATA_GENERAL; + if (sysctl(name, 6, ifmdall, &len, (void *)0, 0) == -1) + err(1, "sysctl IFMIB_IFALLDATA"); + + interesting = NULL; + interesting_row = 0; + for (i = 0; i < ifcount; i++) { + struct ifmibdata *ifmd = ifmdall + i; + + if (interface && strcmp(ifmd->ifmd_name, interface) == 0) { + if ((interesting = calloc(ifcount, + sizeof(struct iftot))) == NULL) + err(1, "malloc failed"); + interesting_row = if_nametoindex(interface); + snprintf(interesting->ift_name, 16, "(%s)", + ifmd->ifmd_name);; + } + } + if ((total = calloc(1, sizeof(struct iftot))) == NULL) + err(1, "malloc failed"); + + if ((sum = calloc(1, sizeof(struct iftot))) == NULL) + err(1, "malloc failed"); + + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void)signal(SIGALRM, catchalarm); + signalled = NO; + (void)setitimer(ITIMER_REAL, &timer_interval, NULL); + first = 1; +banner: + if (vflag > 0) + printf("%9s", " "); + + if (prioflag >= 0) + printf("%39s %39s %36s", "input", + interesting ? interesting->ift_name : "(Total)", "output"); + else + printf("%17s %14s %16s", "input", + interesting ? interesting->ift_name : "(Total)", "output"); + putchar('\n'); + + if (vflag > 0) + printf("%9s", " "); + + printf("%10s %5s %10s ", "packets", "errs", "bytes"); + if (prioflag >= 0) + printf(" %10s %10s %10s %10s", + "tcpkts", "tcbytes", "pvpkts", "pvbytes"); + printf("%10s %5s %10s %5s", "packets", "errs", "bytes", "colls"); + if (dflag) + printf(" %5.5s", "drops"); + if (prioflag >= 0) + printf(" %10s %10s %10s %10s", + "tcpkts", "tcbytes", "pvpkts", "pvbytes"); + if (Fflag) + printf(" %10s %10s", "fpackets", "fbytes"); + putchar('\n'); + fflush(stdout); + line = 0; +loop: + if (vflag && !first) + print_time(); + + if (interesting != NULL) { + struct ifmibdata ifmd; + struct ifmibdata_supplemental ifmsupp; + + len = sizeof(struct ifmibdata); + name[3] = IFMIB_IFDATA; + name[4] = interesting_row; + name[5] = IFDATA_GENERAL; + if (sysctl(name, 6, &ifmd, &len, (void *)0, 0) == -1) + err(1, "sysctl IFDATA_GENERAL %d", interesting_row); + + len = sizeof(struct ifmibdata_supplemental); + name[3] = IFMIB_IFDATA; + name[4] = interesting_row; + name[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(name, 6, &ifmsupp, &len, (void *)0, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL %d", + interesting_row); + + if (!first) { + printf("%10llu %5llu %10llu ", + ifmd.ifmd_data.ifi_ipackets - interesting->ift_ip, + ifmd.ifmd_data.ifi_ierrors - interesting->ift_ie, + ifmd.ifmd_data.ifi_ibytes - interesting->ift_ib); + switch (prioflag) { + case SO_TC_BE: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ibepackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ibebytes - + interesting->ift_itcb); + break; + case SO_TC_BK: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ibkpackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ibkbytes - + interesting->ift_itcb); + break; + case SO_TC_VI: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ivipackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ivibytes - + interesting->ift_itcb); + break; + case SO_TC_VO: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ivopackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ivobytes - + interesting->ift_itcb); + break; + default: + break; + } + if (prioflag >= 0) { + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ipvpackets - + interesting->ift_ipvp, + ifmsupp.ifmd_traffic_class.ifi_ipvbytes - + interesting->ift_ipvb); + } + printf("%10llu %5llu %10llu %5llu", + ifmd.ifmd_data.ifi_opackets - interesting->ift_op, + ifmd.ifmd_data.ifi_oerrors - interesting->ift_oe, + ifmd.ifmd_data.ifi_obytes - interesting->ift_ob, + ifmd.ifmd_data.ifi_collisions - interesting->ift_co); + if (dflag) + printf(" %5llu", + ifmd.ifmd_snd_drops - interesting->ift_dr); + switch (prioflag) { + case SO_TC_BE: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_obepackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_obebytes - + interesting->ift_otcb); + break; + case SO_TC_BK: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_obkpackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_obkbytes - + interesting->ift_otcb); + break; + case SO_TC_VI: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_ovipackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_ovibytes - + interesting->ift_otcb); + break; + case SO_TC_VO: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_ovopackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_ovobytes - + interesting->ift_otcb); + break; + default: + break; + } + if (prioflag >= 0) { + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_opvpackets - + interesting->ift_opvp, + ifmsupp.ifmd_traffic_class.ifi_opvbytes - + interesting->ift_opvb); + } + if (Fflag) { + printf("%10llu %10llu", + ifmsupp.ifmd_data_extended.ifi_fpackets - + interesting->ift_fp, + ifmsupp.ifmd_data_extended.ifi_fbytes - + interesting->ift_fb); + } + } + interesting->ift_ip = ifmd.ifmd_data.ifi_ipackets; + interesting->ift_ie = ifmd.ifmd_data.ifi_ierrors; + interesting->ift_ib = ifmd.ifmd_data.ifi_ibytes; + interesting->ift_op = ifmd.ifmd_data.ifi_opackets; + interesting->ift_oe = ifmd.ifmd_data.ifi_oerrors; + interesting->ift_ob = ifmd.ifmd_data.ifi_obytes; + interesting->ift_co = ifmd.ifmd_data.ifi_collisions; + interesting->ift_dr = ifmd.ifmd_snd_drops; + + /* private counters */ + switch (prioflag) { + case SO_TC_BE: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ibepackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ibebytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_obepackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_obebytes; + break; + case SO_TC_BK: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ibkpackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ibkbytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_obkpackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_obkbytes; + break; + case SO_TC_VI: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ivipackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ivibytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_ovipackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_ovibytes; + break; + case SO_TC_VO: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ivopackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ivobytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_ovopackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_ovobytes; + break; + default: + break; + } + if (prioflag >= 0) { + interesting->ift_ipvp = + ifmsupp.ifmd_traffic_class.ifi_ipvpackets; + interesting->ift_ipvb = + ifmsupp.ifmd_traffic_class.ifi_ipvbytes; + interesting->ift_opvp = + ifmsupp.ifmd_traffic_class.ifi_opvpackets; + interesting->ift_opvb = + ifmsupp.ifmd_traffic_class.ifi_opvbytes; + } + interesting->ift_fp = ifmsupp.ifmd_data_extended.ifi_fpackets; + interesting->ift_fb = ifmsupp.ifmd_data_extended.ifi_fbytes; + } else { + unsigned int latest_ifcount; + struct ifmibdata_supplemental *ifmsuppall = NULL; + + len = sizeof(int); + name[3] = IFMIB_SYSTEM; + name[4] = IFMIB_IFCOUNT; + if (sysctl(name, 5, &latest_ifcount, &len, 0, 0) == 1) + err(1, "sysctl IFMIB_IFCOUNT"); + if (latest_ifcount > ifcount) { + ifcount = latest_ifcount; + len = ifcount * sizeof(struct ifmibdata); + free(ifmdall); + ifmdall = malloc(len); + if (ifmdall == 0) + err(1, "malloc ifmdall failed"); + } else if (latest_ifcount > ifcount) { + ifcount = latest_ifcount; + len = ifcount * sizeof(struct ifmibdata); + } + len = ifcount * sizeof(struct ifmibdata); + name[3] = IFMIB_IFALLDATA; + name[4] = 0; + name[5] = IFDATA_GENERAL; + if (sysctl(name, 6, ifmdall, &len, (void *)0, 0) == -1) + err(1, "sysctl IFMIB_IFALLDATA"); + + len = ifcount * sizeof(struct ifmibdata_supplemental); + ifmsuppall = malloc(len); + if (ifmsuppall == NULL) + err(1, "malloc ifmsuppall failed"); + name[3] = IFMIB_IFALLDATA; + name[4] = 0; + name[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(name, 6, ifmsuppall, &len, (void *)0, 0) == -1) + err(1, "sysctl IFMIB_IFALLDATA SUPPLEMENTAL"); + + sum->ift_ip = 0; + sum->ift_ie = 0; + sum->ift_ib = 0; + sum->ift_op = 0; + sum->ift_oe = 0; + sum->ift_ob = 0; + sum->ift_co = 0; + sum->ift_dr = 0; + sum->ift_itcp = 0; + sum->ift_itcb = 0; + sum->ift_otcp = 0; + sum->ift_otcb = 0; + sum->ift_ipvp = 0; + sum->ift_ipvb = 0; + sum->ift_opvp = 0; + sum->ift_opvb = 0; + sum->ift_fp = 0; + sum->ift_fb = 0; + for (i = 0; i < ifcount; i++) { + struct ifmibdata *ifmd = ifmdall + i; + struct ifmibdata_supplemental *ifmsupp = ifmsuppall + i; + + sum->ift_ip += ifmd->ifmd_data.ifi_ipackets; + sum->ift_ie += ifmd->ifmd_data.ifi_ierrors; + sum->ift_ib += ifmd->ifmd_data.ifi_ibytes; + sum->ift_op += ifmd->ifmd_data.ifi_opackets; + sum->ift_oe += ifmd->ifmd_data.ifi_oerrors; + sum->ift_ob += ifmd->ifmd_data.ifi_obytes; + sum->ift_co += ifmd->ifmd_data.ifi_collisions; + sum->ift_dr += ifmd->ifmd_snd_drops; + /* private counters */ + if (prioflag >= 0) { + switch (prioflag) { + case SO_TC_BE: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ibepackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ibebytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_obepackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_obebytes; + break; + case SO_TC_BK: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ibkpackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ibkbytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_obkpackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_obkbytes; + break; + case SO_TC_VI: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ivipackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ivibytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_ovipackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_ovibytes; + break; + case SO_TC_VO: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ivopackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ivobytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_ovopackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_ovobytes; + break; + default: + break; + } + sum->ift_ipvp += ifmsupp->ifmd_traffic_class.ifi_ipvpackets; + sum->ift_ipvb += ifmsupp->ifmd_traffic_class.ifi_ipvbytes; + sum->ift_opvp += ifmsupp->ifmd_traffic_class.ifi_opvpackets; + sum->ift_opvb += ifmsupp->ifmd_traffic_class.ifi_opvbytes; + } + sum->ift_fp += ifmsupp->ifmd_data_extended.ifi_fpackets; + sum->ift_fb += ifmsupp->ifmd_data_extended.ifi_fbytes; + } + if (!first) { + printf("%10llu %5llu %10llu ", + sum->ift_ip - total->ift_ip, + sum->ift_ie - total->ift_ie, + sum->ift_ib - total->ift_ib); + if (prioflag >= 0) + printf(" %10llu %10llu %10llu %10llu", + sum->ift_itcp - total->ift_itcp, + sum->ift_itcb - total->ift_itcb, + sum->ift_ipvp - total->ift_ipvp, + sum->ift_ipvb - total->ift_ipvb); + printf("%10llu %5llu %10llu %5llu", + sum->ift_op - total->ift_op, + sum->ift_oe - total->ift_oe, + sum->ift_ob - total->ift_ob, + sum->ift_co - total->ift_co); + if (dflag) + printf(" %5llu", sum->ift_dr - total->ift_dr); + if (prioflag >= 0) + printf(" %10llu %10llu %10llu %10llu", + sum->ift_otcp - total->ift_otcp, + sum->ift_otcb - total->ift_otcb, + sum->ift_opvp - total->ift_opvp, + sum->ift_opvb - total->ift_opvb); + if (Fflag) + printf(" %10llu %10llu", + sum->ift_fp - total->ift_fp, + sum->ift_fb - total->ift_fb); + } + *total = *sum; + + free(ifmsuppall); + } + if (!first) + putchar('\n'); + fflush(stdout); + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void)sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void)sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + line++; + first = 0; + if (line == 21) + goto banner; + else + goto loop; + /*NOTREACHED*/ +} + +void +intervalpr(void (*pr)(uint32_t, char *, int), uint32_t off, char *name , int af) +{ + struct itimerval timer_interval; + sigset_t sigset, oldsigset; + + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + + for (;;) { + pr(off, name, af); + + fflush(stdout); + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + signalled = NO; + } +} + +/* + * Called if an interval expires before sidewaysintpr has completed a loop. + * Sets a flag to not wait for the alarm. + */ +static void +catchalarm(int signo ) +{ + signalled = YES; +} + +static char * +sec2str(total) + time_t total; +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + + return(result); +} + +void +intpr_ri(void (*pfunc)(char *)) +{ + int mib[6]; + char *buf = NULL, *lim, *next; + size_t len; + unsigned int ifindex = 0; + struct if_msghdr2 *if2m; + + if (interface != 0) { + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + printf("interface name is not valid: %s\n", interface); + exit(1); + } + } + + mib[0] = CTL_NET; /* networking subsystem */ + mib[1] = PF_ROUTE; /* type of information */ + mib[2] = 0; /* protocol (IPPROTO_xxx) */ + mib[3] = 0; /* address family */ + mib[4] = NET_RT_IFLIST2; /* operation */ + mib[5] = 0; + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return; + if ((buf = malloc(len)) == NULL) { + printf("malloc failed\n"); + exit(1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + free(buf); + return; + } + + printf("%-6s %-17s %8.8s %-9.9s %4s %4s", + "Proto", "Linklayer Address", "Netif", "Expire", "Refs", + "Prbs"); + if (xflag) + printf(" %7s %7s %7s", "RSSI", "LQM", "NPM"); + printf("\n"); + + lim = buf + len; + if2m = (struct if_msghdr2 *)buf; + + for (next = buf; next < lim; ) { + if2m = (struct if_msghdr2 *)next; + next += if2m->ifm_msglen; + + if (if2m->ifm_type != RTM_IFINFO2) + continue; + else if (interface != 0 && if2m->ifm_index != ifindex) + continue; + + llreach_sysctl(if2m->ifm_index); + } + free(buf); +} + +static void +llreach_sysctl(uint32_t ifindex) +{ +#define MAX_SYSCTL_TRY 5 + int mib[6], i, ntry = 0; + size_t mibsize, len, needed, cnt; + struct if_llreach_info *lri; + struct timeval time; + char *buf; + char ifname[IF_NAMESIZE]; + + bzero(&mib, sizeof (mib)); + mibsize = sizeof (mib) / sizeof (mib[0]); + if (sysctlnametomib("net.link.generic.system.llreach_info", mib, + &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + + mibsize = sizeof (mib) / sizeof (mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.link.generic.system.llreach_info"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + cnt = len / sizeof (*lri); + lri = (struct if_llreach_info *)buf; + + gettimeofday(&time, 0); + if (if_indextoname(ifindex, ifname) == NULL) + snprintf(ifname, sizeof (ifname), "%s", "?"); + + for (i = 0; i < cnt; i++, lri++) { + printf("0x%-4x %-17s %8.8s ", lri->lri_proto, + ether_ntoa((struct ether_addr *)lri->lri_addr), ifname); + + if (lri->lri_expire > time.tv_sec) + printf("%-9.9s", sec2str(lri->lri_expire - time.tv_sec)); + else if (lri->lri_expire == 0) + printf("%-9.9s", "permanent"); + else + printf("%-9.9s", "expired"); + + printf(" %4d", lri->lri_refcnt); + if (lri->lri_probes) + printf(" %4d", lri->lri_probes); + + if (xflag) { + if (!lri->lri_probes) + printf(" %-4.4s", "none"); + + if (lri->lri_rssi != IFNET_RSSI_UNKNOWN) + printf(" %7d", lri->lri_rssi); + else + printf(" %-7.7s", "unknown"); + + switch (lri->lri_lqm) + { + case IFNET_LQM_THRESH_OFF: + printf(" %-7.7s", "off"); + break; + case IFNET_LQM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_LQM_THRESH_POOR: + printf(" %-7.7s", "poor"); + break; + case IFNET_LQM_THRESH_GOOD: + printf(" %-7.7s", "good"); + break; + default: + printf(" %7d", lri->lri_lqm); + break; + } + + switch (lri->lri_npm) + { + case IFNET_NPM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_NPM_THRESH_NEAR: + printf(" %-7.7s", "near"); + break; + case IFNET_NPM_THRESH_GENERAL: + printf(" %-7.7s", "general"); + break; + case IFNET_NPM_THRESH_FAR: + printf(" %-7.7s", "far"); + break; + default: + printf(" %7d", lri->lri_npm); + break; + } + } + + printf("\n"); + len -= sizeof (*lri); + } + + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.link.generic.system.llreach_info"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +void +aqstatpr(void) +{ + unsigned int ifindex; + struct itimerval timer_interval; + struct if_qstatsreq ifqr; + struct if_ifclassq_stats *ifcqs; + sigset_t sigset, oldsigset; + u_int32_t scheduler; + int s, n; + + if (cq < -1 || cq >= IFCQ_SC_MAX) { + fprintf(stderr, "Invalid classq index (range is 0-%d)\n", + IFCQ_SC_MAX-1); + return; + } + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + fprintf(stderr, "Invalid interface name\n"); + return; + } + + ifcqs = malloc(sizeof (*ifcqs)); + if (ifcqs == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + return; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("Warning: socket(AF_INET)"); + free(ifcqs); + return; + } + + bzero(&ifqr, sizeof (ifqr)); + strlcpy(ifqr.ifqr_name, interface, sizeof (ifqr.ifqr_name)); + ifqr.ifqr_buf = ifcqs; + ifqr.ifqr_len = sizeof (*ifcqs); + +loop: + if (interval > 0) { + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + } + + ifqr.ifqr_slot = 0; + if (ioctl(s, SIOCGIFQUEUESTATS, (char *)&ifqr) < 0) { + if (errno == ENXIO) { + printf("Queue statistics are not available on %s\n", + interface); + } else { + perror("Warning: ioctl(SIOCGIFQUEUESTATS)"); + } + goto done; + } + scheduler = ifcqs->ifqs_scheduler; + + printf("%s:\n" + " [ sched: %9s qlength: %3d/%3d ]\n", + interface, sched2str(ifcqs->ifqs_scheduler), + ifcqs->ifqs_len, ifcqs->ifqs_maxlen); + printf(" [ pkts: %10llu bytes: %10llu " + " dropped pkts: %6llu bytes: %6llu ]\n", + ifcqs->ifqs_xmitcnt.packets, ifcqs->ifqs_xmitcnt.bytes, + ifcqs->ifqs_dropcnt.packets, ifcqs->ifqs_dropcnt.bytes); + + for (n = 0; n < IFCQ_SC_MAX && scheduler != PKTSCHEDT_NONE; n++) { + if (cq >= 0 && cq != n) + continue; + + ifqr.ifqr_slot = n; + if (ioctl(s, SIOCGIFQUEUESTATS, (char *)&ifqr) < 0) { + perror("Warning: ioctl(SIOCGIFQUEUESTATS)"); + goto done; + } + + update_avg(ifcqs, &qstats[n]); + + switch (scheduler) { + case PKTSCHEDT_FQ_CODEL: + print_fq_codel_stats(n, + &ifcqs->ifqs_fq_codel_stats, + &qstats[n]); + break; + case PKTSCHEDT_NONE: + default: + break; + } + } + + fflush(stdout); + + if (interval > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + goto loop; + } + +done: + free(ifcqs); + close(s); +} + +static void +print_fq_codel_stats(int pri, struct fq_codel_classstats *fqst, + struct queue_stats *qs) +{ + int i = 0; + + if (fqst->fcls_service_class == 0 && fqst->fcls_pri == 0) + return; + printf("=====================================================\n"); + printf(" [ pri: %s (%d)\tsrv_cl: 0x%x\tquantum: %d\tdrr_max: %d ]\n", + pri2str(fqst->fcls_pri), fqst->fcls_pri, + fqst->fcls_service_class, fqst->fcls_quantum, + fqst->fcls_drr_max); + printf(" [ queued pkts: %llu\tbytes: %llu ]\n", + fqst->fcls_pkt_cnt, fqst->fcls_byte_cnt); + printf(" [ dequeued pkts: %llu\tbytes: %llu ]\n", + fqst->fcls_dequeue, fqst->fcls_dequeue_bytes); + printf(" [ budget: %lld\ttarget qdelay: %10s\t", + fqst->fcls_budget, nsec_to_str(fqst->fcls_target_qdelay)); + printf("update interval:%10s ]\n", + nsec_to_str(fqst->fcls_update_interval)); + printf(" [ flow control: %u\tfeedback: %u\tstalls: %u\tfailed: %u ]\n", + fqst->fcls_flow_control, fqst->fcls_flow_feedback, + fqst->fcls_dequeue_stall, fqst->fcls_flow_control_fail); + printf(" [ drop overflow: %llu\tearly: %llu\tmemfail: %u\tduprexmt:%u ]\n", + fqst->fcls_drop_overflow, fqst->fcls_drop_early, + fqst->fcls_drop_memfailure, fqst->fcls_dup_rexmts); + printf(" [ flows total: %u\tnew: %u\told: %u ]\n", + fqst->fcls_flows_cnt, + fqst->fcls_newflows_cnt, fqst->fcls_oldflows_cnt); + printf(" [ throttle on: %u\toff: %u\tdrop: %u ]\n", + fqst->fcls_throttle_on, fqst->fcls_throttle_off, + fqst->fcls_throttle_drops); + printf(" [ compressible pkts: %u compressed pkts: %u]\n", + fqst->fcls_pkts_compressible, fqst->fcls_pkts_compressed); + + if (qflag < 2) + return; + + if (fqst->fcls_flowstats_cnt > 0) { + printf("Flowhash\tBytes\tMin qdelay\tFlags\t\n"); + for (i = 0; i < fqst->fcls_flowstats_cnt; i++) { + printf("%u\t%u\t%14s\t", + fqst->fcls_flowstats[i].fqst_flowhash, + fqst->fcls_flowstats[i].fqst_bytes, + nsec_to_str(fqst->fcls_flowstats[i].fqst_min_qdelay)); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_OLD_FLOW) + printf("O"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_NEW_FLOW) + printf("N"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_LARGE_FLOW) + printf("L"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_DELAY_HIGH) + printf("D"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_FLOWCTL_ON) + printf("F"); + printf("\n"); + } + } +} + +static void +update_avg(struct if_ifclassq_stats *ifcqs, struct queue_stats *qs) +{ + u_int64_t b, p; + int n; + + n = qs->avgn; + + switch (ifcqs->ifqs_scheduler) { + case PKTSCHEDT_FQ_CODEL: + b = ifcqs->ifqs_fq_codel_stats.fcls_dequeue_bytes; + p = ifcqs->ifqs_fq_codel_stats.fcls_dequeue; + break; + default: + b = 0; + p = 0; + break; + } + + if (n == 0) { + qs->prev_bytes = b; + qs->prev_packets = p; + qs->avgn++; + return; + } + + if (b >= qs->prev_bytes) + qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + + (b - qs->prev_bytes)) / n; + + if (p >= qs->prev_packets) + qs->avg_packets = ((qs->avg_packets * (n - 1)) + + (p - qs->prev_packets)) / n; + + qs->prev_bytes = b; + qs->prev_packets = p; + if (n < AVGN_MAX) + qs->avgn++; +} + +#define NSEC_PER_SEC 1000000000 /* nanoseconds per second */ +#define USEC_PER_SEC 1000000 /* microseconds per second */ +#define MSEC_PER_SEC 1000 /* milliseconds per second */ + +static char * +nsec_to_str(unsigned long long nsec) +{ + static char buf[32]; + const char *u; + long double n = nsec, t; + + if (nsec >= NSEC_PER_SEC) { + t = n / NSEC_PER_SEC; + u = "sec "; + } else if (n >= USEC_PER_SEC) { + t = n / USEC_PER_SEC; + u = "msec"; + } else if (n >= MSEC_PER_SEC) { + t = n / MSEC_PER_SEC; + u = "usec"; + } else { + t = n; + u = "nsec"; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); + return (buf); +} + +static char * +sched2str(unsigned int s) +{ + char *c; + + switch (s) { + case PKTSCHEDT_NONE: + c = "NONE"; + break; + case PKTSCHEDT_FQ_CODEL: + c = "FQ_CODEL"; + break; + default: + c = "UNKNOWN"; + break; + } + + return (c); +} + +static char * +pri2str(unsigned int i) +{ + char *c; + switch (i) { + case 9: + c = "BK_SYS"; + break; + case 8: + c = "BK"; + break; + case 7: + c = "BE"; + break; + case 6: + c = "RD"; + break; + case 5: + c = "OAM"; + break; + case 4: + c = "AV"; + break; + case 3: + c = "RV"; + break; + case 2: + c = "VI"; + break; + case 1: + c = "VO"; + break; + case 0: + c = "CTL"; + break; + default: + c = "?"; + break; + } + return (c); +} + +void +rxpollstatpr(void) +{ + struct ifmibdata_supplemental ifmsupp; + size_t miblen = sizeof (ifmsupp); + struct itimerval timer_interval; + struct if_rxpoll_stats *sp; + struct if_netif_stats *np; + sigset_t sigset, oldsigset; + unsigned int ifindex; + int name[6]; + + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + fprintf(stderr, "Invalid interface name\n"); + return; + } + + bzero(&ifmsupp, sizeof (struct ifmibdata_supplemental)); + +loop: + if (interval > 0) { + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + } + + /* Common OID prefix */ + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = ifindex; + name[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(name, 6, &ifmsupp, &miblen, NULL, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL"); + + sp = &ifmsupp.ifmd_rxpoll_stats; + + printf("%-4s [ poll on requests: %15u errors: %27u ]\n", + interface, sp->ifi_poll_on_req, sp->ifi_poll_on_err); + printf(" [ poll off requests: %15u errors: %27u ]\n", + sp->ifi_poll_off_req, sp->ifi_poll_off_err); + printf(" [ polled packets: %18llu per poll limit: %19u ]\n", + sp->ifi_poll_packets, sp->ifi_poll_packets_limit); + printf(" [ polled bytes: %20llu ]\n", sp->ifi_poll_bytes); + printf(" [ poll interval: %14llu nsec ]\n", + sp->ifi_poll_interval_time); + printf(" [ sampled packets avg/min/max: %12u / %12u / %12u ]\n", + sp->ifi_poll_packets_avg, sp->ifi_poll_packets_min, + sp->ifi_poll_packets_max); + printf(" [ sampled bytes avg/min/max: %12u / %12u / %12u ]\n", + sp->ifi_poll_bytes_avg, sp->ifi_poll_bytes_min, + sp->ifi_poll_bytes_max); + printf(" [ sampled wakeups avg: %12u ]\n", + sp->ifi_poll_wakeups_avg); + printf(" [ packets lowat/hiwat threshold: %10u / %10u ]\n", + sp->ifi_poll_packets_lowat, sp->ifi_poll_packets_hiwat); + printf(" [ bytes lowat/hiwat threshold: %10u / %10u ]\n", + sp->ifi_poll_bytes_lowat, sp->ifi_poll_bytes_hiwat); + printf(" [ wakeups lowat/hiwat threshold: %10u / %10u ]\n", + sp->ifi_poll_wakeups_lowat, sp->ifi_poll_wakeups_hiwat); + + np = &ifmsupp.ifmd_netif_stats; + printf(" [ mit mode: %24U cfg idx: %26u ]\n", + np->ifn_rx_mit_mode, np->ifn_rx_mit_cfg_idx); + printf(" [ cfg packets lo/hi threshold: %12u / %12u ]\n", + np->ifn_rx_mit_cfg_packets_lowat, np->ifn_rx_mit_cfg_packets_hiwat); + printf(" [ cfg bytes lo/hi threshold: %12u / %12u ]\n", + np->ifn_rx_mit_cfg_bytes_lowat, np->ifn_rx_mit_cfg_bytes_hiwat); + printf(" [ cfg interval: %15u nsec ]\n", + np->ifn_rx_mit_cfg_interval); + printf(" [ mit interval: %15llu nsec ]\n", + np->ifn_rx_mit_interval); + printf(" [ mit packets avg/min/max: %12u / %12u / %12u ]\n", + np->ifn_rx_mit_packets_avg, np->ifn_rx_mit_packets_min, + np->ifn_rx_mit_packets_max); + printf(" [ mit bytes avg/min/max: %12u / %12u / %12u ]\n", + np->ifn_rx_mit_bytes_avg, np->ifn_rx_mit_bytes_min, + np->ifn_rx_mit_bytes_max); + + fflush(stdout); + + if (interval > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + goto loop; + } +} + +static int +create_control_socket(const char *control_name) +{ + struct sockaddr_ctl sc; + struct ctl_info ctl; + int fd; + + fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (fd == -1) { + perror("socket(PF_SYSTEM)"); + return fd; + } + + /* Get the control ID for statistics */ + bzero(&ctl, sizeof(ctl)); + strlcpy(ctl.ctl_name, control_name, sizeof(ctl.ctl_name)); + if (ioctl(fd, CTLIOCGINFO, &ctl) == -1) + { + perror("ioctl(CTLIOCGINFO)"); + close(fd); + return -1; + } + + /* Connect to the statistics control */ + bzero(&sc, sizeof(sc)); + sc.sc_len = sizeof(sc); + sc.sc_family = AF_SYSTEM; + sc.ss_sysaddr = SYSPROTO_CONTROL; + sc.sc_id = ctl.ctl_id; + sc.sc_unit = 0; + if (connect(fd, (struct sockaddr*)&sc, sc.sc_len) != 0) + { + perror("connect(SYSPROTO_CONTROL)"); + close(fd); + return -1; + } + + /* Set socket to non-blocking operation */ + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1) { + perror("fcnt(F_SETFL,O_NONBLOCK)"); + close(fd); + return -1; + } + return fd; +} + +static int +add_nstat_src(int fd, const nstat_ifnet_add_param *ifparam, + nstat_src_ref_t *outsrc) +{ + nstat_msg_add_src_req *addreq; + nstat_msg_src_added *addedmsg; + nstat_ifnet_add_param *param; + char buffer[sizeof(*addreq) + sizeof(*param)]; + ssize_t result; + const u_int32_t addreqsize = + offsetof(struct nstat_msg_add_src, param) + sizeof(*param); + + /* Setup the add source request */ + addreq = (nstat_msg_add_src_req *)buffer; + param = (nstat_ifnet_add_param*)addreq->param; + bzero(addreq, addreqsize); + addreq->hdr.context = (uintptr_t)&buffer; + addreq->hdr.type = NSTAT_MSG_TYPE_ADD_SRC; + addreq->provider = NSTAT_PROVIDER_IFNET; + bzero(param, sizeof(*param)); + param->ifindex = ifparam->ifindex; + param->threshold = ifparam->threshold; + + /* Send the add source request */ + result = send(fd, addreq, addreqsize, 0); + if (result != addreqsize) + { + if (result == -1) + perror("send(NSTAT_ADD_SRC_REQ)"); + else + fprintf(stderr, "%s: could only sent %ld out of %d\n", + __func__, result, addreqsize); + return -1; + } + + /* Receive the response */ + addedmsg = (nstat_msg_src_added *)buffer; + result = recv(fd, addedmsg, sizeof(buffer), 0); + if (result < sizeof(*addedmsg)) + { + if (result == -1) + perror("recv(NSTAT_ADD_SRC_RSP)"); + else + fprintf(stderr, "%s: recv too small, received %ld, " + "expected %lu\n", __func__, result, + sizeof(*addedmsg)); + return -1; + } + + if (addedmsg->hdr.type != NSTAT_MSG_TYPE_SRC_ADDED) + { + fprintf(stderr, "%s: received wrong message type, received %u " + "expected %u\n", __func__, addedmsg->hdr.type, + NSTAT_MSG_TYPE_SRC_ADDED); + return -1; + } + + if (addedmsg->hdr.context != (uintptr_t)&buffer) + { + fprintf(stderr, "%s: received wrong context, received %llu " + "expected %lu\n", __func__, addedmsg->hdr.context, + (uintptr_t)&buffer); + return -1; + } + *outsrc = addedmsg->srcref; + return 0; +} + +static int +rem_nstat_src(int fd, nstat_src_ref_t sref) +{ + nstat_msg_rem_src_req *remreq; + nstat_msg_src_removed *remrsp; + char buffer[sizeof(*remreq)]; + ssize_t result; + + /* Setup the add source request */ + remreq = (nstat_msg_rem_src_req *)buffer; + bzero(remreq, sizeof(*remreq)); + remreq->hdr.type = NSTAT_MSG_TYPE_REM_SRC; + remreq->srcref = sref; + + /* Send the remove source request */ + result = send(fd, remreq, sizeof(*remreq), 0); + if (result != sizeof(*remreq)) { + if (result == -1) + perror("send(NSTAT_REM_SRC_REQ)"); + else + fprintf(stderr, "%s: could only sent %ld out of %lu\n", + __func__, result, sizeof(*remreq)); + return -1; + } + + /* Receive the response */ + remrsp = (nstat_msg_src_removed *)buffer; + result = recv(fd, remrsp, sizeof(buffer), 0); + if (result < sizeof(*remrsp)) { + if (result == -1) + perror("recv(NSTAT_REM_SRC_RSP)"); + else + fprintf(stderr, "%s: recv too small, received %ld, " + "expected %lu\n", __func__, result, + sizeof(*remrsp)); + return -1; + } + + if (remrsp->hdr.type != NSTAT_MSG_TYPE_SRC_REMOVED) { + fprintf(stderr, "%s: received wrong message type, received %u " + "expected %u\n", __func__, remrsp->hdr.type, + NSTAT_MSG_TYPE_SRC_REMOVED); + return -1; + } + + if (remrsp->srcref != sref) { + fprintf(stderr, "%s: received invalid srcref, received %llu " + "expected %llu\n", __func__, remrsp->srcref, sref); + } + return 0; +} + +static int +get_src_decsription(int fd, nstat_src_ref_t srcref, + struct nstat_ifnet_descriptor *ifdesc) +{ + nstat_msg_get_src_description *dreq; + nstat_msg_src_description *drsp; + char buffer[sizeof(*drsp) + sizeof(*ifdesc)]; + ssize_t result; + const u_int32_t descsize = + offsetof(struct nstat_msg_src_description, data) + + sizeof(nstat_ifnet_descriptor); + + dreq = (nstat_msg_get_src_description *)buffer; + bzero(dreq, sizeof(*dreq)); + dreq->hdr.type = NSTAT_MSG_TYPE_GET_SRC_DESC; + dreq->srcref = srcref; + result = send(fd, dreq, sizeof(*dreq), 0); + if (result != sizeof(*dreq)) + { + if (result == -1) + perror("send(NSTAT_GET_SRC_DESC_REQ)"); + else + fprintf(stderr, "%s: sent %ld out of %lu\n", + __func__, result, sizeof(*dreq)); + return -1; + } + + /* Receive the source description response */ + drsp = (nstat_msg_src_description *)buffer; + result = recv(fd, drsp, sizeof(buffer), 0); + if (result < descsize) + { + if (result == -1) + perror("recv(NSTAT_GET_SRC_DESC_RSP"); + else + fprintf(stderr, "%s: recv too small, received %ld, " + "expected %u\n", __func__, result, descsize); + return -1; + } + + if (drsp->hdr.type != NSTAT_MSG_TYPE_SRC_DESC) + { + fprintf(stderr, "%s: received wrong message type, received %u " + "expected %u\n", __func__, drsp->hdr.type, + NSTAT_MSG_TYPE_SRC_DESC); + return -1; + } + + if (drsp->srcref != srcref) + { + fprintf(stderr, "%s: received message for wrong source, " + "received 0x%llx expected 0x%llx\n", + __func__, drsp->srcref, srcref); + return -1; + } + + bcopy(drsp->data, ifdesc, sizeof(*ifdesc)); + return 0; +} + +static void +print_wifi_status(nstat_ifnet_desc_wifi_status *status) +{ + int tmp; +#define val(x, f) \ + ((status->valid_bitmask & NSTAT_IFNET_DESC_WIFI_ ## f ## _VALID) ?\ + status->x : -1) +#define parg(n, un) #n, val(n, un) +#define pretxtl(n, un) \ + (((tmp = val(n, un)) == -1) ? "(not valid)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_NONE) ? "(none)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_LOW) ? "(low)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_MEDIUM) ? "(medium)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_HIGH) ? "(high)" : \ + "(?)"))))) + + printf("\nwifi status:\n"); + printf( + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d%s\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d\n", + parg(link_quality_metric, LINK_QUALITY_METRIC), + parg(ul_effective_bandwidth, UL_EFFECTIVE_BANDWIDTH), + parg(ul_max_bandwidth, UL_MAX_BANDWIDTH), + parg(ul_min_latency, UL_MIN_LATENCY), + parg(ul_effective_latency, UL_EFFECTIVE_LATENCY), + parg(ul_max_latency, UL_MAX_LATENCY), + parg(ul_retxt_level, UL_RETXT_LEVEL), + pretxtl(ul_retxt_level, UL_RETXT_LEVEL), + parg(ul_bytes_lost, UL_BYTES_LOST), + parg(ul_error_rate, UL_ERROR_RATE), + parg(dl_effective_bandwidth, DL_EFFECTIVE_BANDWIDTH), + parg(dl_max_bandwidth, DL_MAX_BANDWIDTH), + parg(dl_min_latency, DL_MIN_LATENCY), + parg(dl_effective_latency, DL_EFFECTIVE_LATENCY), + parg(dl_max_latency, DL_MAX_LATENCY), + parg(dl_error_rate, DL_ERROR_RATE), + parg(config_frequency, CONFIG_FREQUENCY), + parg(config_multicast_rate, CONFIG_MULTICAST_RATE), + parg(scan_count, CONFIG_SCAN_COUNT), + parg(scan_duration, CONFIG_SCAN_DURATION) + ); +#undef pretxtl +#undef parg +#undef val +} + +static void +print_cellular_status(nstat_ifnet_desc_cellular_status *status) +{ + int tmp, tmp_mss; +#define val(x, f) \ + ((status->valid_bitmask & NSTAT_IFNET_DESC_CELL_ ## f ## _VALID) ?\ + status->x : -1) +#define parg(n, un) #n, val(n, un) +#define pretxtl(n, un) \ + (((tmp = val(n, un)) == -1) ? "(not valid)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_NONE) ? "(none)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_LOW) ? "(low)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_MEDIUM) ? "(medium)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_HIGH) ? "(high)" : \ + "(?)"))))) +#define pretxtm(n, un) \ + (((tmp_mss = val(n,un)) == -1) ? "(not valid)" : \ + ((tmp_mss == NSTAT_IFNET_DESC_MSS_RECOMMENDED_NONE) ? "(none)" : \ + ((tmp_mss == NSTAT_IFNET_DESC_MSS_RECOMMENDED_MEDIUM) ? "(medium)" : \ + ((tmp_mss == NSTAT_IFNET_DESC_MSS_RECOMMENDED_LOW) ? "(low)" : \ + "(?)")))) + + printf("\ncellular status:\n"); + printf( + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d%s\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d %s\n", + parg(link_quality_metric, LINK_QUALITY_METRIC), + parg(ul_effective_bandwidth, UL_EFFECTIVE_BANDWIDTH), + parg(ul_max_bandwidth, UL_MAX_BANDWIDTH), + parg(ul_min_latency, UL_MIN_LATENCY), + parg(ul_effective_latency, UL_EFFECTIVE_LATENCY), + parg(ul_max_latency, UL_MAX_LATENCY), + parg(ul_retxt_level, UL_RETXT_LEVEL), + pretxtl(ul_retxt_level, UL_RETXT_LEVEL), + parg(ul_bytes_lost, UL_BYTES_LOST), + parg(ul_min_queue_size, UL_MIN_QUEUE_SIZE), + parg(ul_avg_queue_size, UL_AVG_QUEUE_SIZE), + parg(ul_max_queue_size, UL_MAX_QUEUE_SIZE), + parg(dl_effective_bandwidth, DL_EFFECTIVE_BANDWIDTH), + parg(dl_max_bandwidth, DL_MAX_BANDWIDTH), + parg(config_inactivity_time, CONFIG_INACTIVITY_TIME), + parg(config_backoff_time, CONFIG_BACKOFF_TIME), + parg(mss_recommended, MSS_RECOMMENDED), + pretxtm(mss_recommended, MSS_RECOMMENDED) + ); +#undef pretxtl +#undef parg +#undef val +} + +static int +get_interface_state(int fd, const char *ifname, struct ifreq *ifr) +{ + bzero(ifr, sizeof(*ifr)); + snprintf(ifr->ifr_name, sizeof(ifr->ifr_name), "%s", ifname); + + if (ioctl(fd, SIOCGIFINTERFACESTATE, ifr) == -1) { + perror("ioctl(CTLIOCGINFO)"); + return -1; + } + return 0; +} + +static void +print_interface_state(struct ifreq *ifr) +{ + int lqm, rrc, avail; + + printf("\ninterface state:\n"); + + if (ifr->ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_LQM_STATE_VALID) { + printf("\tlqm: "); + lqm = ifr->ifr_interface_state.lqm_state; + if (lqm == IFNET_LQM_THRESH_GOOD) + printf("\"good\""); + else if (lqm == IFNET_LQM_THRESH_POOR) + printf("\"poor\""); + else if (lqm == IFNET_LQM_THRESH_BAD) + printf("\"bad\""); + else if (lqm == IFNET_LQM_THRESH_UNKNOWN) + printf("\"unknown\""); + else if (lqm == IFNET_LQM_THRESH_OFF) + printf("\"off\""); + else + printf("invalid(%d)", lqm); + } + + if (ifr->ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_RRC_STATE_VALID) { + printf("\trrc: "); + rrc = ifr->ifr_interface_state.rrc_state; + if (rrc == IF_INTERFACE_STATE_RRC_STATE_CONNECTED) + printf("\"connected\""); + else if (rrc == IF_INTERFACE_STATE_RRC_STATE_IDLE) + printf("\"idle\""); + else + printf("\"invalid(%d)\"", rrc); + } + + if (ifr->ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) { + printf("\tavailability: "); + avail = ifr->ifr_interface_state.interface_availability; + if (avail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE) + printf("\"true\""); + else if (rrc == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE) + printf("\"false\""); + else + printf("\"invalid(%d)\"", avail); + } +} + +void +print_link_status(const char *ifname) +{ + unsigned int ifindex; + struct itimerval timer_interval; + sigset_t sigset, oldsigset; + struct nstat_ifnet_descriptor ifdesc; + nstat_ifnet_add_param ifparam; + nstat_src_ref_t sref = 0; + struct ifreq ifr; + int ctl_fd; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + fprintf(stderr, "Invalid interface name\n"); + return; + } + + if ((ctl_fd = create_control_socket(NET_STAT_CONTROL_NAME)) < 0) + return; + + ifparam.ifindex = ifindex; + ifparam.threshold = UINT64_MAX; + if (add_nstat_src(ctl_fd, &ifparam, &sref)) + goto done; +loop: + if (interval > 0) { + /* create a timer that fires repeatedly every interval + * seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + } + + /* get interface state */ + if (get_interface_state(ctl_fd, ifname, &ifr)) + goto done; + + /* get ntstat interface description */ + if (get_src_decsription(ctl_fd, sref, &ifdesc)) + goto done; + + /* print time */ + printf("\n%s: ", ifname); + print_time(); + + /* print interface state */ + print_interface_state(&ifr); + + /* print ntsat interface link status */ + if (ifdesc.link_status.link_status_type == + NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR) + print_cellular_status(&ifdesc.link_status.u.cellular); + else if (ifdesc.link_status.link_status_type == + NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI) + print_wifi_status(&ifdesc.link_status.u.wifi); + + fflush(stdout); + + if (interval > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + goto loop; + } +done: + if (sref) + rem_nstat_src(ctl_fd, sref); + close(ctl_fd); +} diff --git a/network_cmds/netstat.tproj/inet.c b/network_cmds/netstat.tproj/inet.c new file mode 100644 index 0000000..68e4ddb --- /dev/null +++ b/network_cmds/netstat.tproj/inet.c @@ -0,0 +1,1361 @@ +/* + * Copyright (c) 2008-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + ** @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1988, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <net/if_arp.h> +#include <net/net_perf.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#ifdef INET6 +#include <netinet/ip6.h> +#endif /* INET6 */ +#include <netinet/in_pcb.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> +#include <netinet/igmp_var.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <netinet/tcp_seq.h> +#define TCPSTATES +#include <netinet/tcp_fsm.h> +#include <netinet/tcp_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> + +#include <arpa/inet.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#define ROUNDUP64(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint64_t) - 1))) : sizeof(uint64_t)) +#define ADVANCE64(x, n) (((char *)x) += ROUNDUP64(n)) + +char *inetname (struct in_addr *); +void inetprint (struct in_addr *, int, char *, int); +#ifdef INET6 +extern void inet6print (struct in6_addr *, int, char *, int); +static int udp_done, tcp_done; +extern int mptcp_done; +#endif /* INET6 */ + +#ifdef SRVCACHE +typedef struct __table_private table_t; + +extern table_t *_nc_table_new(uint32_t n); +extern void _nc_table_free(table_t *tin); + +extern void _nc_table_insert(table_t *t, const char *key, void *datum); +extern void *_nc_table_find(table_t *t, const char *key); +extern void _nc_table_delete(table_t *t, const char *key); + +static table_t *_serv_cache = NULL; + +/* + * Read and cache all known services + */ +static void +_serv_cache_open() +{ + struct servent *s; + char *key, *name, *test; + + if (_serv_cache != NULL) return; + + _serv_cache = _nc_table_new(8192); + setservent(0); + + while (NULL != (s = getservent())) + { + if (s->s_name == NULL) continue; + key = NULL; + asprintf(&key, "%hu/%s", (unsigned short)ntohs(s->s_port), s->s_proto); + name = strdup(s->s_name); + test = _nc_table_find(_serv_cache, key); + if (test == NULL) _nc_table_insert(_serv_cache, key, name); + free(key); + } + + endservent(); +} + +void +_serv_cache_close() +{ + _nc_table_free(_serv_cache); + _serv_cache = NULL; +} + +struct servent * +_serv_cache_getservbyport(int port, char *proto) +{ + static struct servent s; + char *key; + unsigned short p; + + _serv_cache_open(); + + memset(&s, 0, sizeof(struct servent)); + asprintf(&key, "%u/%s", port, (proto == NULL) ? "udp" : proto); + + s.s_name = _nc_table_find(_serv_cache, key); + free(key); + if (s.s_name == NULL) return NULL; + + p = port; + s.s_port = htons(p); + s.s_proto = proto; + return &s; +} + +#endif /* SRVCACHE */ + +/* + * Print a summary of connections related to an Internet + * protocol. For TCP, also give state of connection. + * Listening processes (aflag) are suppressed unless the + * -a (all) flag is specified. + */ + +struct xgen_n { + u_int32_t xgn_len; /* length of this structure */ + u_int32_t xgn_kind; /* number of PCBs at this time */ +}; + +#define ALL_XGN_KIND_INP (XSO_SOCKET | XSO_RCVBUF | XSO_SNDBUF | XSO_STATS | XSO_INPCB) +#define ALL_XGN_KIND_TCP (ALL_XGN_KIND_INP | XSO_TCPCB) + +void +protopr(uint32_t proto, /* for sysctl version we pass proto # */ + char *name, int af) +{ + int istcp; + static int first = 1; + char *buf, *next; + const char *mibvar; + struct xinpgen *xig, *oxig; + struct xgen_n *xgn; + size_t len; + struct xtcpcb_n *tp = NULL; + struct xinpcb_n *inp = NULL; + struct xsocket_n *so = NULL; + struct xsockbuf_n *so_rcv = NULL; + struct xsockbuf_n *so_snd = NULL; + struct xsockstat_n *so_stat = NULL; + int which = 0; + + istcp = 0; + switch (proto) { + case IPPROTO_TCP: +#ifdef INET6 + if (tcp_done != 0) + return; + else + tcp_done = 1; +#endif + istcp = 1; + mibvar = "net.inet.tcp.pcblist_n"; + break; + case IPPROTO_UDP: +#ifdef INET6 + if (udp_done != 0) + return; + else + udp_done = 1; +#endif + mibvar = "net.inet.udp.pcblist_n"; + break; + case IPPROTO_DIVERT: + mibvar = "net.inet.divert.pcblist_n"; + break; + default: + mibvar = "net.inet.raw.pcblist_n"; + break; + } + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + /* + * Bail-out to avoid logic error in the loop below when + * there is in fact no more control block to process + */ + if (len <= sizeof(struct xinpgen)) { + free(buf); + return; + } + + oxig = xig = (struct xinpgen *)buf; + for (next = buf + ROUNDUP64(xig->xig_len); next < buf + len; next += ROUNDUP64(xgn->xgn_len)) { + + xgn = (struct xgen_n*)next; + if (xgn->xgn_len <= sizeof(struct xinpgen)) + break; + + if ((which & xgn->xgn_kind) == 0) { + which |= xgn->xgn_kind; + switch (xgn->xgn_kind) { + case XSO_SOCKET: + so = (struct xsocket_n *)xgn; + break; + case XSO_RCVBUF: + so_rcv = (struct xsockbuf_n *)xgn; + break; + case XSO_SNDBUF: + so_snd = (struct xsockbuf_n *)xgn; + break; + case XSO_STATS: + so_stat = (struct xsockstat_n *)xgn; + break; + case XSO_INPCB: + inp = (struct xinpcb_n *)xgn; + break; + case XSO_TCPCB: + tp = (struct xtcpcb_n *)xgn; + break; + default: + printf("unexpected kind %d\n", xgn->xgn_kind); + break; + } + } else { + if (vflag) + printf("got %d twice\n", xgn->xgn_kind); + } + + if ((istcp && which != ALL_XGN_KIND_TCP) || (!istcp && which != ALL_XGN_KIND_INP)) + continue; + which = 0; + + /* Ignore sockets for protocols other than the desired one. */ + if (so->xso_protocol != (int)proto) + continue; + + /* Ignore PCBs which were freed during copyout. */ + if (inp->inp_gencnt > oxig->xig_gen) + continue; + + if ((af == AF_INET && (inp->inp_vflag & INP_IPV4) == 0) +#ifdef INET6 + || (af == AF_INET6 && (inp->inp_vflag & INP_IPV6) == 0) +#endif /* INET6 */ + || (af == AF_UNSPEC && ((inp->inp_vflag & INP_IPV4) == 0 +#ifdef INET6 + && (inp->inp_vflag & + INP_IPV6) == 0 +#endif /* INET6 */ + )) + ) + continue; + + /* + * Local address is not an indication of listening socket or + * server sockey but just rather the socket has been bound. + * That why many UDP sockets were not displayed in the original code. + */ + if (!aflag && istcp && tp->t_state <= TCPS_LISTEN) + continue; + + if (Lflag && !so->so_qlimit) + continue; + + if (first) { + if (!Lflag) { + printf("Active Internet connections"); + if (aflag) + printf(" (including servers)"); + } else + printf( + "Current listen queue sizes (qlen/incqlen/maxqlen)"); + putchar('\n'); + if (Aflag) { + printf("%-16.16s ", "Socket"); + printf("%-9.9s", "Flowhash"); + } + if (Lflag) + printf("%-14.14s %-22.22s\n", + "Listen", "Local Address"); + else { + printf((Aflag && !Wflag) ? + "%-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %-11.11s" : + "%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %-11.11s", + "Proto", "Recv-Q", "Send-Q", + "Local Address", "Foreign Address", + "(state)"); + if (bflag > 0) + printf(" %10.10s %10.10s", "rxbytes", "txbytes"); + if (prioflag >= 0) + printf(" %7.7s[%1d] %7.7s[%1d]", "rxbytes", prioflag, "txbytes", prioflag); + if (vflag > 0) + printf(" %6.6s %6.6s %6.6s %6.6s %6s %10s", + "rhiwat", "shiwat", "pid", "epid", "state", "options"); + printf("\n"); + } + first = 0; + } + if (Aflag) { + if (istcp) + printf("%16lx ", (u_long)inp->inp_ppcb); + else + printf("%16lx ", (u_long)so->so_pcb); + printf("%8x ", inp->inp_flowhash); + } + if (Lflag) { + char buf[15]; + + snprintf(buf, 15, "%d/%d/%d", so->so_qlen, + so->so_incqlen, so->so_qlimit); + printf("%-14.14s ", buf); + } + else { + const char *vchar; + +#ifdef INET6 + if ((inp->inp_vflag & INP_IPV6) != 0) + vchar = ((inp->inp_vflag & INP_IPV4) != 0) + ? "46" : "6 "; + else +#endif + vchar = ((inp->inp_vflag & INP_IPV4) != 0) + ? "4 " : " "; + + printf("%-3.3s%-2.2s %6u %6u ", name, vchar, + so_rcv->sb_cc, + so_snd->sb_cc); + } + if (nflag) { + if (inp->inp_vflag & INP_IPV4) { + inetprint(&inp->inp_laddr, (int)inp->inp_lport, + name, 1); + if (!Lflag) + inetprint(&inp->inp_faddr, + (int)inp->inp_fport, name, 1); + } +#ifdef INET6 + else if (inp->inp_vflag & INP_IPV6) { + inet6print(&inp->in6p_laddr, + (int)inp->inp_lport, name, 1); + if (!Lflag) + inet6print(&inp->in6p_faddr, + (int)inp->inp_fport, name, 1); + } /* else nothing printed now */ +#endif /* INET6 */ + } else if (inp->inp_flags & INP_ANONPORT) { + if (inp->inp_vflag & INP_IPV4) { + inetprint(&inp->inp_laddr, (int)inp->inp_lport, + name, 1); + if (!Lflag) + inetprint(&inp->inp_faddr, + (int)inp->inp_fport, name, 0); + } +#ifdef INET6 + else if (inp->inp_vflag & INP_IPV6) { + inet6print(&inp->in6p_laddr, + (int)inp->inp_lport, name, 1); + if (!Lflag) + inet6print(&inp->in6p_faddr, + (int)inp->inp_fport, name, 0); + } /* else nothing printed now */ +#endif /* INET6 */ + } else { + if (inp->inp_vflag & INP_IPV4) { + inetprint(&inp->inp_laddr, (int)inp->inp_lport, + name, 0); + if (!Lflag) + inetprint(&inp->inp_faddr, + (int)inp->inp_fport, name, + inp->inp_lport != + inp->inp_fport); + } +#ifdef INET6 + else if (inp->inp_vflag & INP_IPV6) { + inet6print(&inp->in6p_laddr, + (int)inp->inp_lport, name, 0); + if (!Lflag) + inet6print(&inp->in6p_faddr, + (int)inp->inp_fport, name, + inp->inp_lport != + inp->inp_fport); + } /* else nothing printed now */ +#endif /* INET6 */ + } + if (istcp && !Lflag) { + if (tp->t_state < 0 || tp->t_state >= TCP_NSTATES) + printf("%-11d", tp->t_state); + else { + printf("%-11s", tcpstates[tp->t_state]); + } + } + if (!istcp) + printf("%-11s", " "); + if (bflag > 0) { + int i; + u_int64_t rxbytes = 0; + u_int64_t txbytes = 0; + + for (i = 0; i < SO_TC_STATS_MAX; i++) { + rxbytes += so_stat->xst_tc_stats[i].rxbytes; + txbytes += so_stat->xst_tc_stats[i].txbytes; + } + + printf(" %10llu %10llu", rxbytes, txbytes); + } + if (prioflag >= 0) { + printf(" %10llu %10llu", + prioflag < SO_TC_STATS_MAX ? so_stat->xst_tc_stats[prioflag].rxbytes : 0, + prioflag < SO_TC_STATS_MAX ? so_stat->xst_tc_stats[prioflag].txbytes : 0); + } + if (vflag > 0) { + printf(" %6u %6u %6u %6u 0x%04x 0x%08x", + so_rcv->sb_hiwat, + so_snd->sb_hiwat, + so->so_last_pid, + so->so_e_pid, + so->so_state, + so->so_options); + } + putchar('\n'); + } + if (xig != oxig && xig->xig_gen != oxig->xig_gen) { + if (oxig->xig_count > xig->xig_count) { + printf("Some %s sockets may have been deleted.\n", + name); + } else if (oxig->xig_count < xig->xig_count) { + printf("Some %s sockets may have been created.\n", + name); + } else { + printf("Some %s sockets may have been created or deleted", + name); + } + } + free(buf); +} + +/* + * Dump TCP statistics structure. + */ +void +tcp_stats(uint32_t off , char *name, int af) +{ + static struct tcpstat ptcpstat; + struct tcpstat tcpstat; + size_t len = sizeof tcpstat; + static uint32_t r_swcsum, pr_swcsum; + static uint32_t t_swcsum, pt_swcsum; + + if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.tcp.stats"); + return; + } + +#ifdef INET6 + if (tcp_done != 0 && interval == 0) + return; + else + tcp_done = 1; +#endif + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define TCPDIFF(f) (tcpstat.f - ptcpstat.f) +#define p(f, m) if (TCPDIFF(f) || sflag <= 1) \ + printf(m, TCPDIFF(f), plural(TCPDIFF(f))) +#define p1a(f, m) if (TCPDIFF(f) || sflag <= 1) \ + printf(m, TCPDIFF(f)) +#define p2(f1, f2, m) if (TCPDIFF(f1) || TCPDIFF(f2) || sflag <= 1) \ + printf(m, TCPDIFF(f1), plural(TCPDIFF(f1)), TCPDIFF(f2), plural(TCPDIFF(f2))) +#define p2a(f1, f2, m) if (TCPDIFF(f1) || TCPDIFF(f2) || sflag <= 1) \ + printf(m, TCPDIFF(f1), plural(TCPDIFF(f1)), TCPDIFF(f2)) +#define p3(f, m) if (TCPDIFF(f) || sflag <= 1) \ + printf(m, TCPDIFF(f), plurales(TCPDIFF(f))) + + p(tcps_sndtotal, "\t%u packet%s sent\n"); + p2(tcps_sndpack,tcps_sndbyte, + "\t\t%u data packet%s (%u byte%s)\n"); + p2(tcps_sndrexmitpack, tcps_sndrexmitbyte, + "\t\t%u data packet%s (%u byte%s) retransmitted\n"); + p(tcps_mturesent, "\t\t%u resend%s initiated by MTU discovery\n"); + p2a(tcps_sndacks, tcps_delack, + "\t\t%u ack-only packet%s (%u delayed)\n"); + p(tcps_sndurg, "\t\t%u URG only packet%s\n"); + p(tcps_sndprobe, "\t\t%u window probe packet%s\n"); + p(tcps_sndwinup, "\t\t%u window update packet%s\n"); + p(tcps_sndctrl, "\t\t%u control packet%s\n"); + p(tcps_fcholdpacket, "\t\t%u data packet%s sent after flow control\n"); + p(tcps_synchallenge, "\t\t%u challenge ACK%s sent due to unexpected SYN\n"); + p(tcps_rstchallenge, "\t\t%u challenge ACK%s sent due to unexpected RST\n"); + t_swcsum = tcpstat.tcps_snd_swcsum + tcpstat.tcps_snd6_swcsum; + if ((t_swcsum - pt_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", (t_swcsum - pt_swcsum)); + p2(tcps_snd_swcsum, tcps_snd_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(tcps_snd6_swcsum, tcps_snd6_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + p(tcps_rcvtotal, "\t%u packet%s received\n"); + p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%u ack%s (for %u byte%s)\n"); + p(tcps_rcvdupack, "\t\t%u duplicate ack%s\n"); + p(tcps_rcvacktoomuch, "\t\t%u ack%s for unsent data\n"); + p2(tcps_rcvpack, tcps_rcvbyte, + "\t\t%u packet%s (%u byte%s) received in-sequence\n"); + p2(tcps_rcvduppack, tcps_rcvdupbyte, + "\t\t%u completely duplicate packet%s (%u byte%s)\n"); + p(tcps_pawsdrop, "\t\t%u old duplicate packet%s\n"); + p(tcps_rcvmemdrop, "\t\t%u received packet%s dropped due to low memory\n"); + p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte, + "\t\t%u packet%s with some dup. data (%u byte%s duped)\n"); + p2(tcps_rcvoopack, tcps_rcvoobyte, + "\t\t%u out-of-order packet%s (%u byte%s)\n"); + p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin, + "\t\t%u packet%s (%u byte%s) of data after window\n"); + p(tcps_rcvwinprobe, "\t\t%u window probe%s\n"); + p(tcps_rcvwinupd, "\t\t%u window update packet%s\n"); + p(tcps_recovered_pkts, "\t\t%u packet%s recovered after loss\n"); + p(tcps_rcvafterclose, "\t\t%u packet%s received after close\n"); + p(tcps_badrst, "\t\t%u bad reset%s\n"); + p(tcps_rcvbadsum, "\t\t%u discarded for bad checksum%s\n"); + r_swcsum = tcpstat.tcps_rcv_swcsum + tcpstat.tcps_rcv6_swcsum; + if ((r_swcsum - pr_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", + (r_swcsum - pr_swcsum)); + p2(tcps_rcv_swcsum, tcps_rcv_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(tcps_rcv6_swcsum, tcps_rcv6_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + p(tcps_rcvbadoff, "\t\t%u discarded for bad header offset field%s\n"); + p1a(tcps_rcvshort, "\t\t%u discarded because packet too short\n"); + p(tcps_connattempt, "\t%u connection request%s\n"); + p(tcps_accepts, "\t%u connection accept%s\n"); + p(tcps_badsyn, "\t%u bad connection attempt%s\n"); + p(tcps_listendrop, "\t%u listen queue overflow%s\n"); + p(tcps_connects, "\t%u connection%s established (including accepts)\n"); + p2(tcps_closed, tcps_drops, + "\t%u connection%s closed (including %u drop%s)\n"); + p(tcps_cachedrtt, "\t\t%u connection%s updated cached RTT on close\n"); + p(tcps_cachedrttvar, + "\t\t%u connection%s updated cached RTT variance on close\n"); + p(tcps_cachedssthresh, + "\t\t%u connection%s updated cached ssthresh on close\n"); + p(tcps_usedrtt, "\t\t%u connection%s initialized RTT from route cache\n"); + p(tcps_usedrttvar, + "\t\t%u connection%s initialized RTT variance from route cache\n"); + p(tcps_usedssthresh, + "\t\t%u connection%s initialized ssthresh from route cache\n"); + p(tcps_conndrops, "\t%u embryonic connection%s dropped\n"); + p2(tcps_rttupdated, tcps_segstimed, + "\t%u segment%s updated rtt (of %u attempt%s)\n"); + p(tcps_rexmttimeo, "\t%u retransmit timeout%s\n"); + p(tcps_timeoutdrop, "\t\t%u connection%s dropped by rexmit timeout\n"); + p(tcps_rxtfindrop, "\t\t%u connection%s dropped after retransmitting FIN\n"); + p(tcps_sndrexmitbad, "\t\t%u unnecessary packet retransmissions%s\n"); + p(tcps_persisttimeo, "\t%u persist timeout%s\n"); + p(tcps_persistdrop, "\t\t%u connection%s dropped by persist timeout\n"); + p(tcps_keeptimeo, "\t%u keepalive timeout%s\n"); + p(tcps_keepprobe, "\t\t%u keepalive probe%s sent\n"); + p(tcps_keepdrops, "\t\t%u connection%s dropped by keepalive\n"); + p(tcps_ka_offload_drops, "\t\t%u connection%s dropped by keepalive offload\n"); + p(tcps_predack, "\t%u correct ACK header prediction%s\n"); + p(tcps_preddat, "\t%u correct data packet header prediction%s\n"); +#ifdef TCP_MAX_SACK + /* TCP_MAX_SACK indicates the header has the SACK structures */ + p(tcps_sack_recovery_episode, "\t%u SACK recovery episode%s\n"); + p(tcps_sack_rexmits, + "\t%u segment rexmit%s in SACK recovery episodes\n"); + p(tcps_sack_rexmit_bytes, + "\t%u byte rexmit%s in SACK recovery episodes\n"); + p(tcps_sack_rcv_blocks, + "\t%u SACK option%s (SACK blocks) received\n"); + p(tcps_sack_send_blocks, "\t%u SACK option%s (SACK blocks) sent\n"); + p1a(tcps_sack_sboverflow, "\t%u SACK scoreboard overflow\n"); +#endif /* TCP_MAX_SACK */ + p(tcps_limited_txt, "\t%u limited transmit%s done\n"); + p(tcps_early_rexmt, "\t%u early retransmit%s done\n"); + p(tcps_sack_ackadv, "\t%u time%s cumulative ack advanced along with SACK\n"); + p(tcps_pto, "\t%u probe timeout%s\n"); + p(tcps_rto_after_pto, "\t\t%u time%s retransmit timeout triggered after probe\n"); + p(tcps_probe_if, "\t\t%u time%s probe packets were sent for an interface\n"); + p(tcps_probe_if_conflict, "\t\t%u time%s couldn't send probe packets for an interface\n"); + p(tcps_tlp_recovery, "\t\t%u time%s fast recovery after tail loss\n"); + p(tcps_tlp_recoverlastpkt, "\t\t%u time%s recovered last packet \n"); + p(tcps_pto_in_recovery, "\t\t%u SACK based rescue retransmit%s\n"); + p(tcps_ecn_client_setup, "\t%u client connection%s attempted to negotiate ECN\n"); + p(tcps_ecn_client_success, "\t\t%u client connection%s successfully negotiated ECN\n"); + p(tcps_ecn_not_supported, "\t\t%u time%s graceful fallback to Non-ECN connection\n"); + p(tcps_ecn_lost_syn, "\t\t%u time%s lost ECN negotiating SYN, followed by retransmission\n"); + p(tcps_ecn_server_setup, "\t\t%u server connection%s attempted to negotiate ECN\n"); + p(tcps_ecn_server_success, "\t\t%u server connection%s successfully negotiated ECN\n"); + p(tcps_ecn_lost_synack, "\t\t%u time%s lost ECN negotiating SYN-ACK, followed by retransmission\n"); + p(tcps_ecn_recv_ce, "\t\t%u time%s received congestion experienced (CE) notification\n"); + p(tcps_ecn_recv_ece, "\t\t%u time%s CWR was sent in response to ECE\n"); + p(tcps_ecn_sent_ece, "\t\t%u time%s sent ECE notification\n"); + p(tcps_ecn_conn_recv_ce, "\t\t%u connection%s received CE atleast once\n"); + p(tcps_ecn_conn_recv_ece, "\t\t%u connection%s received ECE atleast once\n"); + p(tcps_ecn_conn_plnoce, "\t\t%u connection%s using ECN have seen packet loss but no CE\n"); + p(tcps_ecn_conn_pl_ce, "\t\t%u connection%s using ECN have seen packet loss and CE\n"); + p(tcps_ecn_conn_nopl_ce, "\t\t%u connection%s using ECN received CE but no packet loss\n"); + p(tcps_ecn_fallback_synloss, "\t\t%u connection%s fell back to non-ECN due to SYN-loss\n"); + p(tcps_ecn_fallback_reorder, "\t\t%u connection%s fell back to non-ECN due to reordering\n"); + p(tcps_ecn_fallback_ce, "\t\t%u connection%s fell back to non-ECN due to excessive CE-markings\n"); + p(tcps_ecn_fallback_droprst, "\t\t%u connection%s fell back caused by connection drop due to RST\n"); + p(tcps_ecn_fallback_droprxmt, "\t\t%u connection%s fell back due to drop after multiple retransmits \n"); + p(tcps_ecn_fallback_synrst, "\t\t%u connection%s fell back due to RST after SYN\n"); + + p(tcps_detect_reordering, "\t%u time%s packet reordering was detected on a connection\n"); + p(tcps_reordered_pkts, "\t\t%u time%s transmitted packets were reordered\n"); + p(tcps_delay_recovery, "\t\t%u time%s fast recovery was delayed to handle reordering\n"); + p(tcps_avoid_rxmt, "\t\t%u time%s retransmission was avoided by delaying recovery\n"); + p(tcps_unnecessary_rxmt, "\t\t%u retransmission%s not needed \n"); + p(tcps_tailloss_rto, "\t%u retransmission%s due to tail loss\n"); + p(tcps_dsack_sent, "\t%u time%s DSACK option was sent\n"); + p(tcps_dsack_recvd, "\t\t%u time%s DSACK option was received\n"); + p(tcps_dsack_disable, "\t\t%u time%s DSACK was disabled on a connection\n"); + p(tcps_dsack_badrexmt, "\t\t%u time%s recovered from bad retransmission using DSACK\n"); + p(tcps_dsack_ackloss,"\t\t%u time%s ignored DSACK due to ack loss\n"); + p(tcps_dsack_recvd_old,"\t\t%u time%s ignored old DSACK options\n"); + p(tcps_pmtudbh_reverted, "\t%u time%s PMTU Blackhole detection, size reverted\n"); + p(tcps_drop_after_sleep, "\t%u connection%s were dropped after long sleep\n"); + p(tcps_nostretchack, "\t%u connection%s had stretch ack algorithm disabled\n"); + + p(tcps_tfo_cookie_sent,"\t%u time%s a TFO-cookie has been announced\n"); + p(tcps_tfo_syn_data_rcv,"\t%u SYN%s with data and a valid TFO-cookie have been received\n"); + p(tcps_tfo_cookie_req_rcv,"\t%u SYN%s with TFO-cookie-request received\n"); + p(tcps_tfo_cookie_invalid,"\t%u time%s an invalid TFO-cookie has been received\n"); + p(tcps_tfo_cookie_req,"\t%u time%s we requested a TFO-cookie\n"); + p(tcps_tfo_cookie_rcv,"\t\t%u time%s the peer announced a TFO-cookie\n"); + p(tcps_tfo_syn_data_sent,"\t%u time%s we combined SYN with data and a TFO-cookie\n"); + p(tcps_tfo_syn_data_acked,"\t\t%u time%s our SYN with data has been acknowledged\n"); + p(tcps_tfo_syn_loss,"\t%u time%s a connection-attempt with TFO fell back to regular TCP\n"); + p(tcps_tfo_blackhole,"\t%u time%s a TFO-connection blackhole'd\n"); + p(tcps_tfo_cookie_wrong,"\t%u time%s a TFO-cookie we sent was wrong\n"); + p(tcps_tfo_no_cookie_rcv,"\t%u time%s did not received a TFO-cookie we asked for\n"); + p(tcps_tfo_heuristics_disable,"\t%u time%s TFO got disabled due to heuristicsn\n"); + p(tcps_tfo_sndblackhole,"\t%u time%s TFO got blackholed in the sending direction\n"); + + p(tcps_mss_to_default,"\t%u time%s maximum segment size was changed to default\n"); + p(tcps_mss_to_medium,"\t%u time%s maximum segment size was changed to medium\n"); + p(tcps_mss_to_low,"\t%u time%s maximum segment size was changed to low\n"); + + p(tcps_timer_drift_le_1_ms,"\t%u timer drift%s less or equal to 1 ms\n"); + p(tcps_timer_drift_le_10_ms,"\t%u timer drift%s less or equal to 10 ms\n"); + p(tcps_timer_drift_le_20_ms,"\t%u timer drift%s less or equal to 20 ms\n"); + p(tcps_timer_drift_le_50_ms,"\t%u timer drift%s less or equal to 50 ms\n"); + p(tcps_timer_drift_le_100_ms,"\t%u timer drift%s less or equal to 100 ms\n"); + p(tcps_timer_drift_le_200_ms,"\t%u timer drift%s less or equal to 200 ms\n"); + p(tcps_timer_drift_le_500_ms,"\t%u timer drift%s less or equal to 500 ms\n"); + p(tcps_timer_drift_le_1000_ms,"\t%u timer drift%s less or equal to 1000 ms\n"); + p(tcps_timer_drift_gt_1000_ms,"\t%u timer drift%s greater than to 1000 ms\n"); + + if (interval > 0) { + bcopy(&tcpstat, &ptcpstat, len); + pr_swcsum = r_swcsum; + pt_swcsum = t_swcsum; + } + +#undef TCPDIFF +#undef p +#undef p1a +#undef p2 +#undef p2a +#undef p3 +} + +/* + * Dump MPTCP statistics + */ +void +mptcp_stats(uint32_t off , char *name, int af) +{ + static struct tcpstat ptcpstat; + struct tcpstat tcpstat; + size_t len = sizeof tcpstat; + + if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.tcp.stats"); + return; + } + +#ifdef INET6 + if (mptcp_done != 0 && interval == 0) + return; + else + mptcp_done = 1; +#endif + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define MPTCPDIFF(f) (tcpstat.f - ptcpstat.f) +#define p(f, m) if (MPTCPDIFF(f) || sflag <= 1) \ + printf(m, MPTCPDIFF(f), plural(MPTCPDIFF(f))) +#define p1a(f, m) if (MPTCPDIFF(f) || sflag <= 1) \ + printf(m, MPTCPDIFF(f)) +#define p2(f1, f2, m) if (MPTCPDIFF(f1) || MPTCPDIFF(f2) || sflag <= 1) \ + printf(m, MPTCPDIFF(f1), plural(MPTCPDIFF(f1)), \ + MPTCPDIFF(f2), plural(MPTCPDIFF(f2))) +#define p2a(f1, f2, m) if (MPTCPDIFF(f1) || MPTCPDIFF(f2) || sflag <= 1) \ + printf(m, MPTCPDIFF(f1), plural(MPTCPDIFF(f1)), MPTCPDIFF(f2)) +#define p3(f, m) if (MPTCPDIFF(f) || sflag <= 1) \ + printf(m, MPTCPDIFF(f), plurales(MPTCPDIFF(f))) + + p(tcps_mp_sndpacks, "\t%u data packet%s sent\n"); + p(tcps_mp_sndbytes, "\t%u data byte%s sent\n"); + p(tcps_mp_rcvtotal, "\t%u data packet%s received\n"); + p(tcps_mp_rcvbytes, "\t%u data byte%s received\n"); + p(tcps_invalid_mpcap, "\t%u packet%s with an invalid MPCAP option\n"); + p(tcps_invalid_joins, "\t%u packet%s with an invalid MPJOIN option\n"); + p(tcps_mpcap_fallback, "\t%u time%s primary subflow fell back to " + "TCP\n"); + p(tcps_join_fallback, "\t%u time%s secondary subflow fell back to " + "TCP\n"); + p(tcps_estab_fallback, "\t%u DSS option drop%s\n"); + p(tcps_invalid_opt, "\t%u other invalid MPTCP option%s\n"); + p(tcps_mp_reducedwin, "\t%u time%s the MPTCP subflow window was reduced\n"); + p(tcps_mp_badcsum, "\t%u bad DSS checksum%s\n"); + p(tcps_mp_oodata, "\t%u time%s received out of order data \n"); + p3(tcps_mp_switches, "\t%u subflow switch%s\n"); + p3(tcps_mp_sel_symtomsd, "\t%u subflow switch%s due to advisory\n"); + p3(tcps_mp_sel_rtt, "\t%u subflow switch%s due to rtt\n"); + p3(tcps_mp_sel_rto, "\t%u subflow switch%s due to rto\n"); + p3(tcps_mp_sel_peer, "\t%u subflow switch%s due to peer\n"); + p3(tcps_mp_num_probes, "\t%u number of subflow probe%s\n"); + + if (interval > 0) { + bcopy(&tcpstat, &ptcpstat, len); + } + +#undef MPTCPDIFF +#undef p +#undef p1a +#undef p2 +#undef p2a +#undef p3 +} + +/* + * Dump UDP statistics structure. + */ +void +udp_stats(uint32_t off , char *name, int af ) +{ + static struct udpstat pudpstat; + struct udpstat udpstat; + size_t len = sizeof udpstat; + uint32_t delivered; + static uint32_t r_swcsum, pr_swcsum; + static uint32_t t_swcsum, pt_swcsum; + + if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.udp.stats"); + return; + } + +#ifdef INET6 + if (udp_done != 0 && interval == 0) + return; + else + udp_done = 1; +#endif + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define UDPDIFF(f) (udpstat.f - pudpstat.f) +#define p(f, m) if (UDPDIFF(f) || sflag <= 1) \ + printf(m, UDPDIFF(f), plural(UDPDIFF(f))) +#define p1a(f, m) if (UDPDIFF(f) || sflag <= 1) \ + printf(m, UDPDIFF(f)) +#define p2(f1, f2, m) if (UDPDIFF(f1) || UDPDIFF(f2) || sflag <= 1) \ + printf(m, UDPDIFF(f1), plural(UDPDIFF(f1)), UDPDIFF(f2), plural(UDPDIFF(f2))) + p(udps_ipackets, "\t%u datagram%s received\n"); + p1a(udps_hdrops, "\t\t%u with incomplete header\n"); + p1a(udps_badlen, "\t\t%u with bad data length field\n"); + p1a(udps_badsum, "\t\t%u with bad checksum\n"); + p1a(udps_nosum, "\t\t%u with no checksum\n"); + r_swcsum = udpstat.udps_rcv_swcsum + udpstat.udps_rcv6_swcsum; + if ((r_swcsum - pr_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", (r_swcsum - pr_swcsum)); + p2(udps_rcv_swcsum, udps_rcv_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(udps_rcv6_swcsum, udps_rcv6_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + p1a(udps_noport, "\t\t%u dropped due to no socket\n"); + p(udps_noportbcast, + "\t\t%u broadcast/multicast datagram%s undelivered\n"); + /* the next statistic is cumulative in udps_noportbcast */ + p(udps_filtermcast, + "\t\t%u time%s multicast source filter matched\n"); + p1a(udps_fullsock, "\t\t%u dropped due to full socket buffers\n"); + p1a(udpps_pcbhashmiss, "\t\t%u not for hashed pcb\n"); + delivered = UDPDIFF(udps_ipackets) - + UDPDIFF(udps_hdrops) - + UDPDIFF(udps_badlen) - + UDPDIFF(udps_badsum) - + UDPDIFF(udps_noport) - + UDPDIFF(udps_noportbcast) - + UDPDIFF(udps_fullsock); + if (delivered || sflag <= 1) + printf("\t\t%u delivered\n", delivered); + p(udps_opackets, "\t%u datagram%s output\n"); + t_swcsum = udpstat.udps_snd_swcsum + udpstat.udps_snd6_swcsum; + if ((t_swcsum - pt_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", (t_swcsum - pt_swcsum)); + p2(udps_snd_swcsum, udps_snd_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(udps_snd6_swcsum, udps_snd6_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + + if (interval > 0) { + bcopy(&udpstat, &pudpstat, len); + pr_swcsum = r_swcsum; + pt_swcsum = t_swcsum; + } + +#undef UDPDIFF +#undef p +#undef p1a +#undef p2 +} + +/* + * Dump IP statistics structure. + */ +void +ip_stats(uint32_t off , char *name, int af ) +{ + static struct ipstat pipstat; + struct ipstat ipstat; + size_t ipstat_len = sizeof ipstat; + + static net_perf_t pout_net_perf, pin_net_perf; + net_perf_t out_net_perf, in_net_perf; + size_t out_net_perf_len = sizeof (out_net_perf); + size_t in_net_perf_len = sizeof (in_net_perf); + + if (sysctlbyname("net.inet.ip.stats", &ipstat, &ipstat_len, 0, 0) < 0) { + warn("sysctl: net.inet.ip.stats"); + return; + } + + if (sysctlbyname("net.inet.ip.output_perf_data", &out_net_perf, &out_net_perf_len, 0, 0) < 0) { + warn("sysctl: net.inet.ip.output_perf_data"); + bzero(&out_net_perf, out_net_perf_len); + } + + if (sysctlbyname("net.inet.ip.input_perf_data", &in_net_perf, &in_net_perf_len, 0, 0) < 0) { + warn("sysctl: net.inet.ip.input_perf_data"); + bzero(&in_net_perf, in_net_perf_len); + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define IPDIFF(f) (ipstat.f - pipstat.f) +#define p(f, m) if (IPDIFF(f) || sflag <= 1) \ + printf(m, IPDIFF(f), plural(IPDIFF(f))) +#define p1a(f, m) if (IPDIFF(f) || sflag <= 1) \ + printf(m, IPDIFF(f)) +#define p2(f1, f2, m) if (IPDIFF(f1) || IPDIFF(f2) || sflag <= 1) \ + printf(m, IPDIFF(f1), plural(IPDIFF(f1)), IPDIFF(f2), plural(IPDIFF(f2))) + + p(ips_total, "\t%u total packet%s received\n"); + p(ips_badsum, "\t\t%u bad header checksum%s\n"); + p2(ips_rcv_swcsum, ips_rcv_swcsum_bytes, + "\t\t%u header%s (%u byte%s) checksummed in software\n"); + p1a(ips_toosmall, "\t\t%u with size smaller than minimum\n"); + p1a(ips_tooshort, "\t\t%u with data size < data length\n"); + p1a(ips_adj, "\t\t%u with data size > data length\n"); + p(ips_adj_hwcsum_clr, + "\t\t\t%u packet%s forced to software checksum\n"); + p1a(ips_toolong, "\t\t%u with ip length > max ip packet size\n"); + p1a(ips_badhlen, "\t\t%u with header length < data size\n"); + p1a(ips_badlen, "\t\t%u with data length < header length\n"); + p1a(ips_badoptions, "\t\t%u with bad options\n"); + p1a(ips_badvers, "\t\t%u with incorrect version number\n"); + p(ips_fragments, "\t\t%u fragment%s received\n"); + p1a(ips_fragdropped, "\t\t\t%u dropped (dup or out of space)\n"); + p1a(ips_fragtimeout, "\t\t\t%u dropped after timeout\n"); + p1a(ips_reassembled, "\t\t\t%u reassembled ok\n"); + p(ips_delivered, "\t\t%u packet%s for this host\n"); + p(ips_noproto, "\t\t%u packet%s for unknown/unsupported protocol\n"); + p(ips_forward, "\t\t%u packet%s forwarded"); + p(ips_fastforward, " (%u packet%s fast forwarded)"); + if (IPDIFF(ips_forward) || sflag <= 1) + putchar('\n'); + p(ips_cantforward, "\t\t%u packet%s not forwardable\n"); + p(ips_notmember, + "\t\t%u packet%s received for unknown multicast group\n"); + p(ips_redirectsent, "\t\t%u redirect%s sent\n"); + p(ips_rxc_collisions, "\t\t%u input packet%s not chained due to collision\n"); + p(ips_rxc_chained, "\t\t%u input packet%s processed in a chain\n"); + p(ips_rxc_notchain, "\t\t%u input packet%s unable to chain\n"); + p(ips_rxc_chainsz_gt2, + "\t\t%u input packet chain%s processed with length greater than 2\n"); + p(ips_rxc_chainsz_gt4, + "\t\t%u input packet chain%s processed with length greater than 4\n"); + p(ips_rxc_notlist, + "\t\t%u input packet%s did not go through list processing path\n"); + + p(ips_rcv_if_weak_match, + "\t\t%u input packet%s that passed the weak ES interface address match\n"); + p(ips_rcv_if_no_match, + "\t\t%u input packet%s with no interface address match\n"); + +#define INPERFDIFF(f) (in_net_perf.f - pin_net_perf.f) + if (INPERFDIFF(np_total_pkts) > 0 && in_net_perf.np_total_usecs > 0) { + printf("\tInput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", INPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", INPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)in_net_perf.np_total_usecs/(double)in_net_perf.np_total_pkts); + printf("\t\tHistogram:\n"); + printf("\t\t\t x <= %u: %llu\n", in_net_perf.np_hist_bars[0], + INPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[0], in_net_perf.np_hist_bars[1], + INPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[1], in_net_perf.np_hist_bars[2], + INPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[2], in_net_perf.np_hist_bars[3], + INPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + in_net_perf.np_hist_bars[3], INPERFDIFF(np_hist5)); + } +#undef INPERFDIFF + + p(ips_localout, "\t%u packet%s sent from this host\n"); + p(ips_rawout, "\t\t%u packet%s sent with fabricated ip header\n"); + p(ips_odropped, + "\t\t%u output packet%s dropped due to no bufs, etc.\n"); + p(ips_noroute, "\t\t%u output packet%s discarded due to no route\n"); + p(ips_fragmented, "\t\t%u output datagram%s fragmented\n"); + p(ips_ofragments, "\t\t%u fragment%s created\n"); + p(ips_cantfrag, "\t\t%u datagram%s that can't be fragmented\n"); + p(ips_nogif, "\t\t%u tunneling packet%s that can't find gif\n"); + p(ips_badaddr, "\t\t%u datagram%s with bad address in header\n"); + p(ips_pktdropcntrl, + "\t\t%u packet%s dropped due to no bufs for control data\n"); + p(ips_necp_policy_drop, "\t\t%u packet%s dropped due to NECP policy\n"); + p2(ips_snd_swcsum, ips_snd_swcsum_bytes, + "\t\t%u header%s (%u byte%s) checksummed in software\n"); + +#define OUTPERFDIFF(f) (out_net_perf.f - pout_net_perf.f) + if (OUTPERFDIFF(np_total_pkts) > 0 && out_net_perf.np_total_usecs > 0) { + printf("\tOutput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", OUTPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", OUTPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)out_net_perf.np_total_usecs/(double)out_net_perf.np_total_pkts); + printf("\t\tHistogram:\n"); + printf("\t\t\t x <= %u: %llu\n", out_net_perf.np_hist_bars[0], + OUTPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[0], out_net_perf.np_hist_bars[1], + OUTPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[1], out_net_perf.np_hist_bars[2], + OUTPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[2], out_net_perf.np_hist_bars[3], + OUTPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + out_net_perf.np_hist_bars[3], OUTPERFDIFF(np_hist5)); + } +#undef OUTPERFDIFF + + if (interval > 0) { + bcopy(&ipstat, &pipstat, ipstat_len); + bcopy(&in_net_perf, &pin_net_perf, in_net_perf_len); + bcopy(&out_net_perf, &pout_net_perf, out_net_perf_len); + } + +#undef IPDIFF +#undef p +#undef p1a +#undef p2 +} + +/* + * Dump ARP statistics structure. + */ +void +arp_stats(uint32_t off, char *name, int af) +{ + static struct arpstat parpstat; + struct arpstat arpstat; + size_t len = sizeof (arpstat); + + if (sysctlbyname("net.link.ether.inet.stats", &arpstat, + &len, 0, 0) < 0) { + warn("sysctl: net.link.ether.inet.stats"); + return; + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define ARPDIFF(f) (arpstat.f - parpstat.f) +#define p(f, m) if (ARPDIFF(f) || sflag <= 1) \ + printf(m, ARPDIFF(f), plural(ARPDIFF(f))) +#define p2(f, m) if (ARPDIFF(f) || sflag <= 1) \ + printf(m, ARPDIFF(f), pluralies(ARPDIFF(f))) +#define p3(f, m) if (ARPDIFF(f) || sflag <= 1) \ + printf(m, ARPDIFF(f), plural(ARPDIFF(f)), pluralies(ARPDIFF(f))) + + p(txrequests, "\t%u broadast ARP request%s sent\n"); + p(txurequests, "\t%u unicast ARP request%s sent\n"); + p2(txreplies, "\t%u ARP repl%s sent\n"); + p(txannounces, "\t%u ARP announcement%s sent\n"); + p(rxrequests, "\t%u ARP request%s received\n"); + p2(rxreplies, "\t%u ARP repl%s received\n"); + p(received, "\t%u total ARP packet%s received\n"); + p(txconflicts, "\t%u ARP conflict probe%s sent\n"); + p(invalidreqs, "\t%u invalid ARP resolve request%s\n"); + p(reqnobufs, "\t%u total packet%s dropped due to lack of memory\n"); + p3(held, "\t%u total packet%s held awaiting ARP repl%s\n"); + p(dropped, "\t%u total packet%s dropped due to no ARP entry\n"); + p(purged, "\t%u total packet%s dropped during ARP entry removal\n"); + p2(timeouts, "\t%u ARP entr%s timed out\n"); + p(dupips, "\t%u Duplicate IP%s seen\n"); + + if (interval > 0) + bcopy(&arpstat, &parpstat, len); + +#undef ARPDIFF +#undef p +#undef p2 +} + +static char *icmpnames[] = { + "echo reply", + "#1", + "#2", + "destination unreachable", + "source quench", + "routing redirect", + "#6", + "#7", + "echo", + "router advertisement", + "router solicitation", + "time exceeded", + "parameter problem", + "time stamp", + "time stamp reply", + "information request", + "information request reply", + "address mask request", + "address mask reply", +}; + +/* + * Dump ICMP statistics. + */ +void +icmp_stats(uint32_t off , char *name, int af ) +{ + static struct icmpstat picmpstat; + struct icmpstat icmpstat; + int i, first; + int mib[4]; /* CTL_NET + PF_INET + IPPROTO_ICMP + req */ + size_t len; + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_ICMP; + mib[3] = ICMPCTL_STATS; + + len = sizeof icmpstat; + memset(&icmpstat, 0, len); + if (sysctl(mib, 4, &icmpstat, &len, (void *)0, 0) < 0) + return; /* XXX should complain, but not traditional */ + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define ICMPDIFF(f) (icmpstat.f - picmpstat.f) +#define p(f, m) if (ICMPDIFF(f) || sflag <= 1) \ + printf(m, ICMPDIFF(f), plural(ICMPDIFF(f))) +#define p1a(f, m) if (ICMPDIFF(f) || sflag <= 1) \ + printf(m, ICMPDIFF(f)) + + p(icps_error, "\t%u call%s to icmp_error\n"); + p(icps_oldicmp, + "\t%u error%s not generated 'cuz old message was icmp\n"); + for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) + if (ICMPDIFF(icps_outhist[i]) != 0) { + if (first) { + printf("\tOutput histogram:\n"); + first = 0; + } + printf("\t\t%s: %u\n", icmpnames[i], + ICMPDIFF(icps_outhist[i])); + } + p(icps_badcode, "\t%u message%s with bad code fields\n"); + p(icps_tooshort, "\t%u message%s < minimum length\n"); + p(icps_checksum, "\t%u bad checksum%s\n"); + p(icps_badlen, "\t%u message%s with bad length\n"); + p1a(icps_bmcastecho, "\t%u multicast echo requests ignored\n"); + p1a(icps_bmcasttstamp, "\t%u multicast timestamp requests ignored\n"); + for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) + if (ICMPDIFF(icps_inhist[i]) != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %u\n", icmpnames[i], + ICMPDIFF(icps_inhist[i])); + } + p(icps_reflect, "\t%u message response%s generated\n"); + +#undef ICMPDIFF +#undef p +#undef p1a + mib[3] = ICMPCTL_MASKREPL; + len = sizeof i; + if (sysctl(mib, 4, &i, &len, (void *)0, 0) < 0) + return; + printf("\tICMP address mask responses are %sabled\n", + i ? "en" : "dis"); + + if (interval > 0) + bcopy(&icmpstat, &picmpstat, sizeof (icmpstat)); +} + +/* + * Dump IGMP statistics structure. + */ +void +igmp_stats(uint32_t off , char *name, int af ) +{ + static struct igmpstat_v3 pigmpstat; + struct igmpstat_v3 igmpstat; + size_t len = sizeof igmpstat; + + if (sysctlbyname("net.inet.igmp.v3stats", &igmpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.igmp.v3stats"); + return; + } + + if (igmpstat.igps_version != IGPS_VERSION_3) { + warnx("%s: version mismatch (%d != %d)", __func__, + igmpstat.igps_version, IGPS_VERSION_3); + } + if (igmpstat.igps_len != IGPS_VERSION3_LEN) { + warnx("%s: size mismatch (%d != %d)", __func__, + igmpstat.igps_len, IGPS_VERSION3_LEN); + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define IGMPDIFF(f) ((uintmax_t)(igmpstat.f - pigmpstat.f)) +#define p64(f, m) if (IGMPDIFF(f) || sflag <= 1) \ + printf(m, IGMPDIFF(f), plural(IGMPDIFF(f))) +#define py64(f, m) if (IGMPDIFF(f) || sflag <= 1) \ + printf(m, IGMPDIFF(f), IGMPDIFF(f) != 1 ? "ies" : "y") + + p64(igps_rcv_total, "\t%ju message%s received\n"); + p64(igps_rcv_tooshort, "\t%ju message%s received with too few bytes\n"); + p64(igps_rcv_badttl, "\t%ju message%s received with wrong TTL\n"); + p64(igps_rcv_badsum, "\t%ju message%s received with bad checksum\n"); + py64(igps_rcv_v1v2_queries, "\t%ju V1/V2 membership quer%s received\n"); + py64(igps_rcv_v3_queries, "\t%ju V3 membership quer%s received\n"); + py64(igps_rcv_badqueries, + "\t%ju membership quer%s received with invalid field(s)\n"); + py64(igps_rcv_gen_queries, "\t%ju general quer%s received\n"); + py64(igps_rcv_group_queries, "\t%ju group quer%s received\n"); + py64(igps_rcv_gsr_queries, "\t%ju group-source quer%s received\n"); + py64(igps_drop_gsr_queries, "\t%ju group-source quer%s dropped\n"); + p64(igps_rcv_reports, "\t%ju membership report%s received\n"); + p64(igps_rcv_badreports, + "\t%ju membership report%s received with invalid field(s)\n"); + p64(igps_rcv_ourreports, +"\t%ju membership report%s received for groups to which we belong\n"); + p64(igps_rcv_nora, "\t%ju V3 report%s received without Router Alert\n"); + p64(igps_snd_reports, "\t%ju membership report%s sent\n"); + + if (interval > 0) + bcopy(&igmpstat, &pigmpstat, len); + +#undef IGMPDIFF +#undef p64 +#undef py64 +} + +/* + * Pretty print an Internet address (net address + port). + */ +void +inetprint(struct in_addr *in, int port, char *proto, int numeric_port) +{ + struct servent *sp = 0; + char line[80], *cp; + int width; + + if (Wflag) + snprintf(line, sizeof(line), "%s.", inetname(in)); + else + snprintf(line, sizeof(line), "%.*s.", (Aflag && !numeric_port) ? 12 : 16, inetname(in)); + cp = index(line, '\0'); + if (!numeric_port && port) +#ifdef _SERVICE_CACHE_ + sp = _serv_cache_getservbyport(port, proto); +#else + sp = getservbyport((int)port, proto); +#endif + if (sp || port == 0) + snprintf(cp, sizeof(line) - (cp - line), "%.15s ", sp ? sp->s_name : "*"); + else + snprintf(cp, sizeof(line) - (cp - line), "%d ", ntohs((u_short)port)); + width = (Aflag && !Wflag) ? 18 : 22; + if (Wflag) + printf("%-*s ", width, line); + else + printf("%-*.*s ", width, width, line); +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +char * +inetname(struct in_addr *inp) +{ + register char *cp; + static char line[MAXHOSTNAMELEN]; + struct hostent *hp; + struct netent *np; + + cp = 0; + if (!nflag && inp->s_addr != INADDR_ANY) { + int net = inet_netof(*inp); + int lna = inet_lnaof(*inp); + + if (lna == INADDR_ANY) { + np = getnetbyaddr(net, AF_INET); + if (np) + cp = np->n_name; + } + if (cp == 0) { + hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET); + if (hp) { + cp = hp->h_name; + //### trimdomain(cp, strlen(cp)); + } + } + } + if (inp->s_addr == INADDR_ANY) + strlcpy(line, "*", sizeof(line)); + else if (cp) { + strlcpy(line, cp, sizeof(line)); + } else { + inp->s_addr = ntohl(inp->s_addr); +#define C(x) ((u_int)((x) & 0xff)) + snprintf(line, sizeof(line), "%u.%u.%u.%u", C(inp->s_addr >> 24), + C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr)); + } + return (line); +} diff --git a/network_cmds/netstat.tproj/inet6.c b/network_cmds/netstat.tproj/inet6.c new file mode 100644 index 0000000..4bc757c --- /dev/null +++ b/network_cmds/netstat.tproj/inet6.c @@ -0,0 +1,1333 @@ +/* + * Copyright (c) 2008-2018 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* BSDI inet.c,v 2.3 1995/10/24 02:19:29 prb Exp */ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/netstat/inet6.c,v 1.3.2.9 2001/08/10 09:07:09 ru Exp $ + */ + +#ifdef INET6 +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/net_perf.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/in_systm.h> +#include <netinet6/in6_pcb.h> +#include <netinet6/in6_var.h> +#include <netinet6/ip6_var.h> +#include <netinet6/raw_ip6.h> + +#include <arpa/inet.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" + +#if defined(__APPLE__) && !defined(__unused) +#define __unused +#endif + +char *inet6name (struct in6_addr *); +void inet6print (struct in6_addr *, int, char *, int); + +static char *ip6nh[] = { + "hop by hop", + "ICMP", + "IGMP", + "#3", + "IP", + "#5", + "TCP", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "UDP", + "#18", + "#19", + "#20", + "#21", + "IDP", + "#23", + "#24", + "#25", + "#26", + "#27", + "#28", + "TP", + "#30", + "#31", + "#32", + "#33", + "#34", + "#35", + "#36", + "#37", + "#38", + "#39", + "#40", + "IP6", + "#42", + "routing", + "fragment", + "#45", + "#46", + "#47", + "#48", + "#49", + "ESP", + "AH", + "#52", + "#53", + "#54", + "#55", + "#56", + "#57", + "ICMP6", + "no next header", + "destination option", + "#61", + "mobility", + "#63", + "#64", + "#65", + "#66", + "#67", + "#68", + "#69", + "#70", + "#71", + "#72", + "#73", + "#74", + "#75", + "#76", + "#77", + "#78", + "#79", + "ISOIP", + "#81", + "#82", + "#83", + "#84", + "#85", + "#86", + "#87", + "#88", + "OSPF", + "#80", + "#91", + "#92", + "#93", + "#94", + "#95", + "#96", + "Ethernet", + "#98", + "#99", + "#100", + "#101", + "#102", + "PIM", + "#104", + "#105", + "#106", + "#107", + "#108", + "#109", + "#110", + "#111", + "#112", + "#113", + "#114", + "#115", + "#116", + "#117", + "#118", + "#119", + "#120", + "#121", + "#122", + "#123", + "#124", + "#125", + "#126", + "#127", + "#128", + "#129", + "#130", + "#131", + "#132", + "#133", + "#134", + "#135", + "#136", + "#137", + "#138", + "#139", + "#140", + "#141", + "#142", + "#143", + "#144", + "#145", + "#146", + "#147", + "#148", + "#149", + "#150", + "#151", + "#152", + "#153", + "#154", + "#155", + "#156", + "#157", + "#158", + "#159", + "#160", + "#161", + "#162", + "#163", + "#164", + "#165", + "#166", + "#167", + "#168", + "#169", + "#170", + "#171", + "#172", + "#173", + "#174", + "#175", + "#176", + "#177", + "#178", + "#179", + "#180", + "#181", + "#182", + "#183", + "#184", + "#185", + "#186", + "#187", + "#188", + "#189", + "#180", + "#191", + "#192", + "#193", + "#194", + "#195", + "#196", + "#197", + "#198", + "#199", + "#200", + "#201", + "#202", + "#203", + "#204", + "#205", + "#206", + "#207", + "#208", + "#209", + "#210", + "#211", + "#212", + "#213", + "#214", + "#215", + "#216", + "#217", + "#218", + "#219", + "#220", + "#221", + "#222", + "#223", + "#224", + "#225", + "#226", + "#227", + "#228", + "#229", + "#230", + "#231", + "#232", + "#233", + "#234", + "#235", + "#236", + "#237", + "#238", + "#239", + "#240", + "#241", + "#242", + "#243", + "#244", + "#245", + "#246", + "#247", + "#248", + "#249", + "#250", + "#251", + "#252", + "#253", + "#254", + "#255", +}; + + +static const char *srcrulenames[IP6S_SRCRULE_COUNT] = { + "default", // IP6S_SRCRULE_0 + "prefer same address", // IP6S_SRCRULE_1 + "prefer appropriate scope", // IP6S_SRCRULE_2 + "avoid deprecated addresses", // IP6S_SRCRULE_3 + "prefer home addresses", // IP6S_SRCRULE_4 + "prefer outgoing interface", // IP6S_SRCRULE_5 + "prefer matching label", // IP6S_SRCRULE_6 + "prefer temporary addresses", // IP6S_SRCRULE_7 + "prefer addresses on alive interfaces", // IP6S_SRCRULE_7x + "use longest matching prefix", // IP6S_SRCRULE_8 + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* + * Dump IP6 statistics structure. + */ +void +ip6_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct ip6stat pip6stat; + struct ip6stat ip6stat; + int first, i; + int mib[4]; + size_t len; + static net_perf_t pout_net_perf, pin_net_perf; + net_perf_t out_net_perf, in_net_perf; + size_t out_net_perf_len = sizeof (out_net_perf); + size_t in_net_perf_len = sizeof (in_net_perf); + + if (sysctlbyname("net.inet6.ip6.output_perf_data", &out_net_perf, &out_net_perf_len, 0, 0) < 0) { + perror("sysctl: net.inet6.ip6.output_perf_data"); + return; + } + + if (sysctlbyname("net.inet6.ip6.input_perf_data", &in_net_perf, &in_net_perf_len, 0, 0) < 0) { + perror("sysctl: net.inet6.ip6.input_perf_data"); + return; + } + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_STATS; + + len = sizeof ip6stat; + memset(&ip6stat, 0, len); + if (sysctl(mib, 4, &ip6stat, &len, (void *)0, 0) < 0) + return; + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define IP6DIFF(f) (ip6stat.f - pip6stat.f) +#define p(f, m) if (IP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)IP6DIFF(f), plural(IP6DIFF(f))) +#define p1a(f, m) if (IP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)IP6DIFF(f)) + + p(ip6s_total, "\t%llu total packet%s received\n"); + p1a(ip6s_toosmall, "\t\t%llu with size smaller than minimum\n"); + p1a(ip6s_tooshort, "\t\t%llu with data size < data length\n"); + p1a(ip6s_adj, "\t\t%llu with data size > data length\n"); + p(ip6s_adj_hwcsum_clr, + "\t\t\t%llu packet%s forced to software checksum\n"); + p1a(ip6s_badoptions, "\t\t%llu with bad options\n"); + p1a(ip6s_badvers, "\t\t%llu with incorrect version number\n"); + p(ip6s_fragments, "\t\t%llu fragment%s received\n"); + p1a(ip6s_fragdropped, + "\t\t\t%llu dropped (dup or out of space)\n"); + p1a(ip6s_fragtimeout, "\t\t\t%llu dropped after timeout\n"); + p1a(ip6s_fragoverflow, "\t\t\t%llu exceeded limit\n"); + p1a(ip6s_reassembled, "\t\t\t%llu reassembled ok\n"); + p1a(ip6s_atmfrag_rcvd, "\t\t\t%llu atomic fragments received\n"); + p(ip6s_delivered, "\t\t%llu packet%s for this host\n"); + p(ip6s_forward, "\t\t%llu packet%s forwarded\n"); + p(ip6s_cantforward, "\t\t%llu packet%s not forwardable\n"); + p(ip6s_redirectsent, "\t\t%llu redirect%s sent\n"); + p(ip6s_notmember, "\t\t%llu multicast packet%s which we don't join\n"); + p(ip6s_exthdrtoolong, + "\t\t%llu packet%s whose headers are not continuous\n"); + p(ip6s_nogif, "\t\t%llu tunneling packet%s that can't find gif\n"); + p(ip6s_toomanyhdr, + "\t\t%llu packet%s discarded due to too may headers\n"); + p1a(ip6s_forward_cachehit, "\t\t%llu forward cache hit\n"); + p1a(ip6s_forward_cachemiss, "\t\t%llu forward cache miss\n"); + p(ip6s_pktdropcntrl, + "\t\t%llu packet%s dropped due to no bufs for control data\n"); + /* CLAT46 input stats */ + p(ip6s_clat464_in_tooshort_drop, + "\t\t%llu input packet%s dropped due to too short length \n"); + p(ip6s_clat464_in_nov6addr_drop, + "\t\t%llu input packet%s dropped due to missing CLAT46 IPv6 address\n"); + p(ip6s_clat464_in_nov4addr_drop, + "\t\t%llu input packet%s dropped due to missing CLAT46 IPv4 address\n"); + p(ip6s_clat464_in_v4synthfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 IPv4 address derivation failure\n"); + p(ip6s_clat464_in_64transfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 IP header translation failure\n"); + p(ip6s_clat464_in_64proto_transfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 protocol translation failure\n"); + p(ip6s_clat464_in_64frag_transfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 fragment translation failure\n"); + p(ip6s_clat464_in_invalpbuf_drop, + "\t\t%llu input packet%s dropped due to invalid pbuf\n"); + p(ip6s_clat464_in_v4_drop, + "\t\t%llu input IPv4 packet%s dropped on CLAT46 enabled interface\n"); + p(ip6s_clat464_in_drop, + "\t\t%llu input packet%s dropped due to CLAT46 failures\n"); + p(ip6s_clat464_in_success, + "\t\t%llu input packet%s successfully translated from IPv6 to IPv4\n"); + +#define INPERFDIFF(f) (in_net_perf.f - pin_net_perf.f) + if (INPERFDIFF(np_total_pkts) > 0 && in_net_perf.np_total_usecs > 0) { + printf("\tInput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", INPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", INPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)in_net_perf.np_total_usecs/(double)in_net_perf.np_total_pkts); + printf("\t\tPerformance Histogram:\n"); + printf("\t\t\t x <= %u: %llu\n", in_net_perf.np_hist_bars[0], + INPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[0], in_net_perf.np_hist_bars[1], + INPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[1], in_net_perf.np_hist_bars[2], + INPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[2], in_net_perf.np_hist_bars[3], + INPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + in_net_perf.np_hist_bars[3], INPERFDIFF(np_hist5)); + } +#undef INPERFDIFF + + p(ip6s_localout, "\t%llu packet%s sent from this host\n"); + p(ip6s_rawout, "\t\t%llu packet%s sent with fabricated ip header\n"); + p(ip6s_odropped, + "\t\t%llu output packet%s dropped due to no bufs, etc.\n"); + p(ip6s_noroute, "\t\t%llu output packet%s discarded due to no route\n"); + p(ip6s_fragmented, "\t\t%llu output datagram%s fragmented\n"); + p(ip6s_ofragments, "\t\t%llu fragment%s created\n"); + p(ip6s_cantfrag, "\t\t%llu datagram%s that can't be fragmented\n"); + p(ip6s_badscope, "\t\t%llu packet%s that violated scope rules\n"); + p(ip6s_necp_policy_drop, "\t\t%llu packet%s dropped due to NECP policy\n"); + /* CLAT46 output stats */ + p(ip6s_clat464_out_nov6addr_drop, + "\t\t%llu output packet%s dropped due to missing CLAT46 IPv6 address\n"); + p(ip6s_clat464_out_v6synthfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 IPv6 address synthesis failure\n"); + p(ip6s_clat464_out_46transfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 IP header translation failure\n"); + p(ip6s_clat464_out_46proto_transfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 protocol translation failure\n"); + p(ip6s_clat464_out_46frag_transfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 fragment translation failure\n"); + p(ip6s_clat464_out_invalpbuf_drop, + "\t\t%llu output packet%s dropped due to invalid pbuf\n"); + p(ip6s_clat464_out_drop, + "\t\t%llu output packet%s dropped due to CLAT46 failures\n"); + p(ip6s_clat464_out_success, + "\t\t%llu output packet%s successfully translated from IPv4 to IPv6\n"); + p(ip6s_rcv_if_weak_match, + "\t\t%llu input packet%s that passed the weak ES interface address match\n"); + p(ip6s_rcv_if_no_match, + "\t\t%llu input packet%s with no interface address match\n"); + +#define OUTPERFDIFF(f) (out_net_perf.f - pout_net_perf.f) + if (OUTPERFDIFF(np_total_pkts) > 0 && out_net_perf.np_total_usecs > 0) { + printf("\tOutput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", OUTPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", OUTPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)out_net_perf.np_total_usecs/(double)out_net_perf.np_total_pkts); + printf("\t\tHistogram:\n"); + printf("\t\t\t x <= %u: %llu\n", out_net_perf.np_hist_bars[0], + OUTPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[0], out_net_perf.np_hist_bars[1], + OUTPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[1], out_net_perf.np_hist_bars[2], + OUTPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[2], out_net_perf.np_hist_bars[3], + OUTPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + out_net_perf.np_hist_bars[3], OUTPERFDIFF(np_hist5)); + } +#undef OUTPERFDIFF + + for (first = 1, i = 0; i < 256; i++) + if (IP6DIFF(ip6s_nxthist[i]) != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", ip6nh[i], + (unsigned long long)IP6DIFF(ip6s_nxthist[i])); + } + printf("\tMbuf statistics:\n"); + printf("\t\t%llu one mbuf\n", (unsigned long long)IP6DIFF(ip6s_m1)); + for (first = 1, i = 0; i < 32; i++) { + char ifbuf[IFNAMSIZ]; + if (IP6DIFF(ip6s_m2m[i]) != 0) { + if (first) { + printf("\t\ttwo or more mbuf:\n"); + first = 0; + } + printf("\t\t\t%s= %llu\n", + if_indextoname(i, ifbuf), + (unsigned long long)IP6DIFF(ip6s_m2m[i])); + } + } + printf("\t\t%llu one ext mbuf\n", + (unsigned long long)IP6DIFF(ip6s_mext1)); + printf("\t\t%llu two or more ext mbuf\n", + (unsigned long long)IP6DIFF(ip6s_mext2m)); + + /* for debugging source address selection */ +#define PRINT_SCOPESTAT(s,i) do {\ + switch(i) { /* XXX hardcoding in each case */\ + case 1:\ + p(s, "\t\t\t%llu node-local%s\n");\ + break;\ + case 2:\ + p(s,"\t\t\t%llu link-local%s\n");\ + break;\ + case 5:\ + p(s,"\t\t\t%llu site-local%s\n");\ + break;\ + case 14:\ + p(s,"\t\t\t%llu global%s\n");\ + break;\ + default:\ + printf("\t\t\t%llu addresses scope=%x\n",\ + (unsigned long long)IP6DIFF(s), i);\ + }\ + } while (0); + + p(ip6s_sources_none, + "\t\t%llu failure%s of source address selection\n"); + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_sameif[i]) || 1) { + if (first) { + printf("\t\tsource addresses on an outgoing I/F\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_sameif[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_otherif[i]) || 1) { + if (first) { + printf("\t\tsource addresses on a non-outgoing I/F\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_otherif[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_samescope[i]) || 1) { + if (first) { + printf("\t\tsource addresses of same scope\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_samescope[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_otherscope[i]) || 1) { + if (first) { + printf("\t\tsource addresses of a different scope\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_otherscope[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_deprecated[i]) || 1) { + if (first) { + printf("\t\tdeprecated source addresses\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_deprecated[i], i); + } + } +#define PRINT_SRCRULESTAT(s,i) do {\ + if (srcrulenames[i] != NULL) \ + printf("\t\t\t%llu rule%s %s\n", \ + (unsigned long long)IP6DIFF(s), \ + plural(IP6DIFF(s)), \ + srcrulenames[i]); \ +} while (0); + + for (first = 1, i = 0; i < IP6S_SRCRULE_COUNT; i++) { + if (IP6DIFF(ip6s_sources_rule[i]) || 1) { + if (first) { + printf("\t\tsource address selection\n"); + first = 0; + } + PRINT_SRCRULESTAT(ip6s_sources_rule[i], i); + } + } + + p(ip6s_dad_collide, "\t\t%llu duplicate address detection collision%s\n"); + + p(ip6s_dad_loopcount, "\t\t%llu duplicate address detection NS loop%s\n"); + + p(ip6s_sources_skip_expensive_secondary_if, "\t\t%llu time%s ignored source on secondary expensive I/F\n"); + + if (interval > 0) { + bcopy(&ip6stat, &pip6stat, len); + bcopy(&in_net_perf, &pin_net_perf, in_net_perf_len); + bcopy(&out_net_perf, &pout_net_perf, out_net_perf_len); + } +#undef IP6DIFF +#undef p +#undef p1a +} + +/* + * Dump IPv6 per-interface statistics based on RFC 2465. + */ +void +ip6_ifstats(char *ifname) +{ + struct in6_ifreq ifr; + int s; +#define p(f, m) if (ifr.ifr_ifru.ifru_stat.f || sflag <= 1) \ + printf(m, (unsigned long long)ifr.ifr_ifru.ifru_stat.f, plural(ifr.ifr_ifru.ifru_stat.f)) +#define p_5(f, m) if (ifr.ifr_ifru.ifru_stat.f || sflag <= 1) \ + printf(m, (unsigned long long)ip6stat.f) + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("Warning: socket(AF_INET6)"); + return; + } + + if (interval && vflag > 0) + print_time(); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + printf("ip6 on %s:\n", ifr.ifr_name); + + if (ioctl(s, SIOCGIFSTAT_IN6, (char *)&ifr) < 0) { + perror("Warning: ioctl(SIOCGIFSTAT_IN6)"); + goto end; + } + + p(ifs6_in_receive, "\t%llu total input datagram%s\n"); + p(ifs6_in_hdrerr, "\t%llu datagram%s with invalid header received\n"); + p(ifs6_in_toobig, "\t%llu datagram%s exceeded MTU received\n"); + p(ifs6_in_noroute, "\t%llu datagram%s with no route received\n"); + p(ifs6_in_addrerr, "\t%llu datagram%s with invalid dst received\n"); + p(ifs6_in_protounknown, "\t%llu datagram%s with unknown proto received\n"); + p(ifs6_in_truncated, "\t%llu truncated datagram%s received\n"); + p(ifs6_in_discard, "\t%llu input datagram%s discarded\n"); + p(ifs6_in_deliver, + "\t%llu datagram%s delivered to an upper layer protocol\n"); + p(ifs6_out_forward, "\t%llu datagram%s forwarded to this interface\n"); + p(ifs6_out_request, + "\t%llu datagram%s sent from an upper layer protocol\n"); + p(ifs6_out_discard, "\t%llu total discarded output datagram%s\n"); + p(ifs6_out_fragok, "\t%llu output datagram%s fragmented\n"); + p(ifs6_out_fragfail, "\t%llu output datagram%s failed on fragment\n"); + p(ifs6_out_fragcreat, "\t%llu output datagram%s succeeded on fragment\n"); + p(ifs6_reass_reqd, "\t%llu incoming datagram%s fragmented\n"); + p(ifs6_reass_ok, "\t%llu datagram%s reassembled\n"); + p(ifs6_atmfrag_rcvd, "\t%llu atomic fragments%s received\n"); + p(ifs6_reass_fail, "\t%llu datagram%s failed on reassembling\n"); + p(ifs6_in_mcast, "\t%llu multicast datagram%s received\n"); + p(ifs6_out_mcast, "\t%llu multicast datagram%s sent\n"); + + p(ifs6_cantfoward_icmp6, "\t%llu ICMPv6 packet%s received for unreachable destination\n"); + p(ifs6_addr_expiry_cnt, "\t%llu address expiry event%s reported\n"); + p(ifs6_pfx_expiry_cnt, "\t%llu prefix expiry event%s reported\n"); + p(ifs6_defrtr_expiry_cnt, "\t%llu default router expiry event%s reported\n"); + end: + close(s); + +#undef p +#undef p_5 +} + +static char *icmp6names[] = { + "#0", + "unreach", + "packet too big", + "time exceed", + "parameter problem", + "#5", + "#6", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "#17", + "#18", + "#19", + "#20", + "#21", + "#22", + "#23", + "#24", + "#25", + "#26", + "#27", + "#28", + "#29", + "#30", + "#31", + "#32", + "#33", + "#34", + "#35", + "#36", + "#37", + "#38", + "#39", + "#40", + "#41", + "#42", + "#43", + "#44", + "#45", + "#46", + "#47", + "#48", + "#49", + "#50", + "#51", + "#52", + "#53", + "#54", + "#55", + "#56", + "#57", + "#58", + "#59", + "#60", + "#61", + "#62", + "#63", + "#64", + "#65", + "#66", + "#67", + "#68", + "#69", + "#70", + "#71", + "#72", + "#73", + "#74", + "#75", + "#76", + "#77", + "#78", + "#79", + "#80", + "#81", + "#82", + "#83", + "#84", + "#85", + "#86", + "#87", + "#88", + "#89", + "#80", + "#91", + "#92", + "#93", + "#94", + "#95", + "#96", + "#97", + "#98", + "#99", + "#100", + "#101", + "#102", + "#103", + "#104", + "#105", + "#106", + "#107", + "#108", + "#109", + "#110", + "#111", + "#112", + "#113", + "#114", + "#115", + "#116", + "#117", + "#118", + "#119", + "#120", + "#121", + "#122", + "#123", + "#124", + "#125", + "#126", + "#127", + "echo", + "echo reply", + "multicast listener query", + "MLDv1 listener report", + "MLDv1 listener done", + "router solicitation", + "router advertisement", + "neighbor solicitation", + "neighbor advertisement", + "redirect", + "router renumbering", + "node information request", + "node information reply", + "inverse neighbor solicitation", + "inverse neighbor advertisement", + "MLDv2 listener report", + "#144", + "#145", + "#146", + "#147", + "#148", + "#149", + "#150", + "#151", + "#152", + "#153", + "#154", + "#155", + "#156", + "#157", + "#158", + "#159", + "#160", + "#161", + "#162", + "#163", + "#164", + "#165", + "#166", + "#167", + "#168", + "#169", + "#170", + "#171", + "#172", + "#173", + "#174", + "#175", + "#176", + "#177", + "#178", + "#179", + "#180", + "#181", + "#182", + "#183", + "#184", + "#185", + "#186", + "#187", + "#188", + "#189", + "#180", + "#191", + "#192", + "#193", + "#194", + "#195", + "#196", + "#197", + "#198", + "#199", + "#200", + "#201", + "#202", + "#203", + "#204", + "#205", + "#206", + "#207", + "#208", + "#209", + "#210", + "#211", + "#212", + "#213", + "#214", + "#215", + "#216", + "#217", + "#218", + "#219", + "#220", + "#221", + "#222", + "#223", + "#224", + "#225", + "#226", + "#227", + "#228", + "#229", + "#230", + "#231", + "#232", + "#233", + "#234", + "#235", + "#236", + "#237", + "#238", + "#239", + "#240", + "#241", + "#242", + "#243", + "#244", + "#245", + "#246", + "#247", + "#248", + "#249", + "#250", + "#251", + "#252", + "#253", + "#254", + "#255", +}; + +/* + * Dump ICMP6 statistics. + */ +void +icmp6_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct icmp6stat picmp6stat; + struct icmp6stat icmp6stat; + register int i, first; + int mib[4]; + size_t len; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_ICMPV6; + mib[3] = ICMPV6CTL_STATS; + + len = sizeof icmp6stat; + memset(&icmp6stat, 0, len); + if (sysctl(mib, 4, &icmp6stat, &len, (void *)0, 0) < 0) + return; + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define ICMP6DIFF(f) (icmp6stat.f - picmp6stat.f) +#define p(f, m) if (ICMP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)ICMP6DIFF(f), plural(ICMP6DIFF(f))) +#define p_5(f, m) printf(m, (unsigned long long)ICMP6DIFF(f)) + + p(icp6s_error, "\t%llu call%s to icmp_error\n"); + p(icp6s_canterror, + "\t%llu error%s not generated because old message was icmp error or so\n"); + p(icp6s_toofreq, + "\t%llu error%s not generated because rate limitation\n"); +#define NELEM (sizeof(icmp6stat.icp6s_outhist)/sizeof(icmp6stat.icp6s_outhist[0])) + for (first = 1, i = 0; i < NELEM; i++) + if (ICMP6DIFF(icp6s_outhist[i]) != 0) { + if (first) { + printf("\tOutput histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", icmp6names[i], + (unsigned long long)ICMP6DIFF(icp6s_outhist[i])); + } +#undef NELEM + p(icp6s_badcode, "\t%llu message%s with bad code fields\n"); + p(icp6s_tooshort, "\t%llu message%s < minimum length\n"); + p(icp6s_checksum, "\t%llu bad checksum%s\n"); + p(icp6s_badlen, "\t%llu message%s with bad length\n"); +#define NELEM (sizeof(icmp6stat.icp6s_inhist)/sizeof(icmp6stat.icp6s_inhist[0])) + for (first = 1, i = 0; i < NELEM; i++) + if (ICMP6DIFF(icp6s_inhist[i]) != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", icmp6names[i], + (unsigned long long)ICMP6DIFF(icp6s_inhist[i])); + } +#undef NELEM + printf("\tHistogram of error messages to be generated:\n"); + p_5(icp6s_odst_unreach_noroute, "\t\t%llu no route\n"); + p_5(icp6s_odst_unreach_admin, "\t\t%llu administratively prohibited\n"); + p_5(icp6s_odst_unreach_beyondscope, "\t\t%llu beyond scope\n"); + p_5(icp6s_odst_unreach_addr, "\t\t%llu address unreachable\n"); + p_5(icp6s_odst_unreach_noport, "\t\t%llu port unreachable\n"); + p_5(icp6s_opacket_too_big, "\t\t%llu packet too big\n"); + p_5(icp6s_otime_exceed_transit, "\t\t%llu time exceed transit\n"); + p_5(icp6s_otime_exceed_reassembly, "\t\t%llu time exceed reassembly\n"); + p_5(icp6s_oparamprob_header, "\t\t%llu erroneous header field\n"); + p_5(icp6s_oparamprob_nextheader, "\t\t%llu unrecognized next header\n"); + p_5(icp6s_oparamprob_option, "\t\t%llu unrecognized option\n"); + p_5(icp6s_oredirect, "\t\t%llu redirect\n"); + p_5(icp6s_ounknown, "\t\t%llu unknown\n"); + + p(icp6s_reflect, "\t%llu message response%s generated\n"); + p(icp6s_nd_toomanyopt, "\t%llu message%s with too many ND options\n"); + p(icp6s_nd_badopt, "\t%qu message%s with bad ND options\n"); + p(icp6s_badns, "\t%qu bad neighbor solicitation message%s\n"); + p(icp6s_badna, "\t%qu bad neighbor advertisement message%s\n"); + p(icp6s_badrs, "\t%qu bad router solicitation message%s\n"); + p(icp6s_badra, "\t%qu bad router advertisement message%s\n"); + p(icp6s_badredirect, "\t%qu bad redirect message%s\n"); + p(icp6s_pmtuchg, "\t%llu path MTU change%s\n"); + p(icp6s_rfc6980_drop, "\t%qu dropped fragmented NDP message%s\n"); + + if (interval > 0) + bcopy(&icmp6stat, &picmp6stat, len); + +#undef ICMP6DIFF +#undef p +#undef p_5 +} + +/* + * Dump ICMPv6 per-interface statistics based on RFC 2466. + */ +void +icmp6_ifstats(char *ifname) +{ + struct in6_ifreq ifr; + int s; +#define p(f, m) if (ifr.ifr_ifru.ifru_icmp6stat.f || sflag <= 1) \ + printf(m, (unsigned long long)ifr.ifr_ifru.ifru_icmp6stat.f, plural(ifr.ifr_ifru.ifru_icmp6stat.f)) + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("Warning: socket(AF_INET6)"); + return; + } + + if (interval && vflag > 0) + print_time(); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + printf("icmp6 on %s:\n", ifr.ifr_name); + + if (ioctl(s, SIOCGIFSTAT_ICMP6, (char *)&ifr) < 0) { + perror("Warning: ioctl(SIOCGIFSTAT_ICMP6)"); + goto end; + } + + p(ifs6_in_msg, "\t%llu total input message%s\n"); + p(ifs6_in_error, "\t%llu total input error message%s\n"); + p(ifs6_in_dstunreach, "\t%llu input destination unreachable error%s\n"); + p(ifs6_in_adminprohib, "\t%llu input administratively prohibited error%s\n"); + p(ifs6_in_timeexceed, "\t%llu input time exceeded error%s\n"); + p(ifs6_in_paramprob, "\t%llu input parameter problem error%s\n"); + p(ifs6_in_pkttoobig, "\t%llu input packet too big error%s\n"); + p(ifs6_in_echo, "\t%llu input echo request%s\n"); + p(ifs6_in_echoreply, "\t%llu input echo reply%s\n"); + p(ifs6_in_routersolicit, "\t%llu input router solicitation%s\n"); + p(ifs6_in_routeradvert, "\t%llu input router advertisement%s\n"); + p(ifs6_in_neighborsolicit, "\t%llu input neighbor solicitation%s\n"); + p(ifs6_in_neighboradvert, "\t%llu input neighbor advertisement%s\n"); + p(ifs6_in_redirect, "\t%llu input redirect%s\n"); + p(ifs6_in_mldquery, "\t%llu input MLD query%s\n"); + p(ifs6_in_mldreport, "\t%llu input MLD report%s\n"); + p(ifs6_in_mlddone, "\t%llu input MLD done%s\n"); + + p(ifs6_out_msg, "\t%llu total output message%s\n"); + p(ifs6_out_error, "\t%llu total output error message%s\n"); + p(ifs6_out_dstunreach, "\t%llu output destination unreachable error%s\n"); + p(ifs6_out_adminprohib, "\t%llu output administratively prohibited error%s\n"); + p(ifs6_out_timeexceed, "\t%llu output time exceeded error%s\n"); + p(ifs6_out_paramprob, "\t%llu output parameter problem error%s\n"); + p(ifs6_out_pkttoobig, "\t%llu output packet too big error%s\n"); + p(ifs6_out_echo, "\t%llu output echo request%s\n"); + p(ifs6_out_echoreply, "\t%llu output echo reply%s\n"); + p(ifs6_out_routersolicit, "\t%llu output router solicitation%s\n"); + p(ifs6_out_routeradvert, "\t%llu output router advertisement%s\n"); + p(ifs6_out_neighborsolicit, "\t%llu output neighbor solicitation%s\n"); + p(ifs6_out_neighboradvert, "\t%llu output neighbor advertisement%s\n"); + p(ifs6_out_redirect, "\t%llu output redirect%s\n"); + p(ifs6_out_mldquery, "\t%llu output MLD query%s\n"); + p(ifs6_out_mldreport, "\t%llu output MLD report%s\n"); + p(ifs6_out_mlddone, "\t%llu output MLD done%s\n"); + + end: + close(s); +#undef p +} + +/* + * Dump raw ip6 statistics structure. + */ +void +rip6_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct rip6stat prip6stat; + struct rip6stat rip6stat; + u_quad_t delivered; + int mib[4]; + size_t l; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_RIP6STATS; + l = sizeof(rip6stat); + if (sysctl(mib, 4, &rip6stat, &l, NULL, 0) < 0) { + perror("Warning: sysctl(net.inet6.ip6.rip6stats)"); + return; + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define RIP6DIFF(f) (rip6stat.f - prip6stat.f) +#define p(f, m) if (RIP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)RIP6DIFF(f), plural(RIP6DIFF(f))) + p(rip6s_ipackets, "\t%llu message%s received\n"); + p(rip6s_isum, "\t%llu checksum calculation%s on inbound\n"); + p(rip6s_badsum, "\t%llu message%s with bad checksum\n"); + p(rip6s_nosock, "\t%llu message%s dropped due to no socket\n"); + p(rip6s_nosockmcast, + "\t%llu multicast message%s dropped due to no socket\n"); + p(rip6s_fullsock, + "\t%llu message%s dropped due to full socket buffers\n"); + delivered = RIP6DIFF(rip6s_ipackets) - + RIP6DIFF(rip6s_badsum) - + RIP6DIFF(rip6s_nosock) - + RIP6DIFF(rip6s_nosockmcast) - + RIP6DIFF(rip6s_fullsock); + if (delivered || sflag <= 1) + printf("\t%llu delivered\n", (unsigned long long)delivered); + p(rip6s_opackets, "\t%llu datagram%s output\n"); + + if (interval > 0) + bcopy(&rip6stat, &prip6stat, l); + +#undef RIP6DIFF +#undef p +} + +/* + * Pretty print an Internet address (net address + port). + * If the nflag was specified, use numbers instead of names. + */ +#ifdef SRVCACHE +extern struct servent * _serv_cache_getservbyport(int port, char *proto); + +#define GETSERVBYPORT6(port, proto, ret)\ +{\ + if (strcmp((proto), "tcp6") == 0)\ + (ret) = _serv_cache_getservbyport((int)(port), "tcp");\ + else if (strcmp((proto), "udp6") == 0)\ + (ret) = _serv_cache_getservbyport((int)(port), "udp");\ + else\ + (ret) = _serv_cache_getservbyport((int)(port), (proto));\ +}; +#else +#define GETSERVBYPORT6(port, proto, ret)\ +{\ + if (strcmp((proto), "tcp6") == 0)\ + (ret) = getservbyport((int)(port), "tcp");\ + else if (strcmp((proto), "udp6") == 0)\ + (ret) = getservbyport((int)(port), "udp");\ + else\ + (ret) = getservbyport((int)(port), (proto));\ +}; +#endif +void +inet6print(struct in6_addr *in6, int port, char *proto, int numeric) +{ + struct servent *sp = 0; + char line[80], *cp; + int width; + + snprintf(line, sizeof(line), "%.*s.", lflag ? 39 : + (Aflag && !numeric) ? 12 : 16, inet6name(in6)); + cp = index(line, '\0'); + if (!numeric && port) + GETSERVBYPORT6(port, proto, sp); + if (sp || port == 0) + snprintf(cp, sizeof(line) - (cp - line), "%.15s", sp ? sp->s_name : "*"); + else + snprintf(cp, sizeof(line) - (cp - line), "%d", ntohs((u_short)port)); + width = lflag ? 45 : Aflag ? 18 : 22; + printf("%-*.*s ", width, width, line); +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ + +char * +inet6name(struct in6_addr *in6p) +{ + register char *cp; + static char line[50]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN]; + static int first = 1; + char hbuf[NI_MAXHOST]; + struct sockaddr_in6 sin6; + const int niflag = NI_NUMERICHOST; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = index(domain, '.'))) + (void) memmove(domain, cp + 1, strlen(cp + 1) + 1); + else + domain[0] = 0; + } + cp = 0; + if (!nflag && !IN6_IS_ADDR_UNSPECIFIED(in6p)) { + hp = gethostbyaddr((char *)in6p, sizeof(*in6p), AF_INET6); + if (hp) { + if ((cp = index(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + if (IN6_IS_ADDR_UNSPECIFIED(in6p)) + strlcpy(line, "*", sizeof(line)); + else if (cp) + strlcpy(line, cp, sizeof(line)); + else { + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *in6p; + + if (IN6_IS_ADDR_LINKLOCAL(in6p) || + IN6_IS_ADDR_MC_NODELOCAL(in6p) || + IN6_IS_ADDR_MC_LINKLOCAL(in6p)) { + sin6.sin6_scope_id = + ntohs(*(u_int16_t *)&in6p->s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } + + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + hbuf, sizeof(hbuf), NULL, 0, niflag) != 0) + strlcpy(hbuf, "?", sizeof(hbuf)); + strlcpy(line, hbuf, sizeof(line)); + } + return (line); +} +#endif /*INET6*/ diff --git a/network_cmds/netstat.tproj/ipsec.c b/network_cmds/netstat.tproj/ipsec.c new file mode 100644 index 0000000..0eab162 --- /dev/null +++ b/network_cmds/netstat.tproj/ipsec.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2008-2012 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* $FreeBSD: src/usr.bin/netstat/ipsec.c,v 1.1.2.3 2001/08/10 09:07:09 ru Exp $ */ +/* $NetBSD: inet.c,v 1.35.2.1 1999/04/29 14:57:08 perry Exp $ */ +/* $KAME: ipsec.c,v 1.25 2001/03/12 09:04:39 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> + +#ifdef IPSEC +#include <netinet6/ipsec.h> +#include <netkey/keysock.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" + +#if defined(__APPLE__) && !defined(__unused) +#define __unused +#endif +/* + * portability issues: + * - bsdi[34] uses PLURAL(), not plural(). + * - freebsd2 can't print "unsigned long long" properly. + */ +/* + * XXX see PORTABILITY for the twist + */ +#define LLU "%llu" +#define CAST unsigned long long + +#ifdef IPSEC +struct val2str { + int val; + const char *str; +}; + +static struct val2str ipsec_ahnames[] = { + { SADB_AALG_NONE, "none", }, + { SADB_AALG_MD5HMAC, "hmac-md5", }, + { SADB_AALG_SHA1HMAC, "hmac-sha1", }, + { SADB_X_AALG_MD5, "md5", }, + { SADB_X_AALG_SHA, "sha", }, + { SADB_X_AALG_NULL, "null", }, +#ifdef SADB_X_AALG_SHA2_256 + { SADB_X_AALG_SHA2_256, "hmac-sha2-256", }, +#endif +#ifdef SADB_X_AALG_SHA2_384 + { SADB_X_AALG_SHA2_384, "hmac-sha2-384", }, +#endif +#ifdef SADB_X_AALG_SHA2_512 + { SADB_X_AALG_SHA2_512, "hmac-sha2-512", }, +#endif + { -1, NULL }, +}; + +static struct val2str ipsec_espnames[] = { + { SADB_EALG_NONE, "none", }, + { SADB_EALG_DESCBC, "des-cbc", }, + { SADB_EALG_3DESCBC, "3des-cbc", }, + { SADB_EALG_NULL, "null", }, +#ifdef SADB_X_EALG_RC5CBC + { SADB_X_EALG_RC5CBC, "rc5-cbc", }, +#endif + { SADB_X_EALG_CAST128CBC, "cast128-cbc", }, + { SADB_X_EALG_BLOWFISHCBC, "blowfish-cbc", }, +#ifdef SADB_X_EALG_RIJNDAELCBC + { SADB_X_EALG_RIJNDAELCBC, "rijndael-cbc", }, +#endif + { -1, NULL }, +}; + +static struct val2str ipsec_compnames[] = { + { SADB_X_CALG_NONE, "none", }, + { SADB_X_CALG_OUI, "oui", }, + { SADB_X_CALG_DEFLATE, "deflate", }, + { SADB_X_CALG_LZS, "lzs", }, + { -1, NULL }, +}; + +static const char *pfkey_msgtypenames[] = { + "reserved", "getspi", "update", "add", "delete", + "get", "acquire", "register", "expire", "flush", + "dump", "x_promisc", "x_pchange", "x_spdupdate", "x_spdadd", + "x_spddelete", "x_spdget", "x_spdacquire", "x_spddump", "x_spdflush", + "x_spdsetidx", "x_spdexpire", "x_spddelete2" +}; + +static struct ipsecstat pipsecstat; +static struct ipsecstat ipsecstat; + +static void print_ipsecstats (void); +static const char *pfkey_msgtype_names (int); +static void ipsec_hist (const u_quad_t *, const u_quad_t *, size_t, + const struct val2str *, const char *); + +/* + * Dump IPSEC statistics structure. + */ +static void +ipsec_hist(const u_quad_t *hist, + const u_quad_t *phist, + size_t histmax, + const struct val2str *name, + const char *title) +{ + int first; + size_t proto; + const struct val2str *p; + + first = 1; + for (proto = 0; proto < histmax; proto++) { + if ((hist[proto] - phist[proto]) <= 0) + continue; + if (first) { + printf("\t%s histogram:\n", title); + first = 0; + } + for (p = name; p && p->str; p++) { + if (p->val == (int)proto) + break; + } + if (p && p->str) { + printf("\t\t%s: " LLU "\n", p->str, + (CAST)hist[proto] - (CAST)phist[proto]); + } else { + printf("\t\t#%ld: " LLU "\n", (long)proto, + (CAST)hist[proto] - (CAST)phist[proto]); + } + } +} + +static void +print_ipsecstats(void) +{ +#define IPSECDIFF(f) (ipsecstat.f - pipsecstat.f) +#define p(f, m) if (IPSECDIFF(f) || sflag <= 1) \ + printf(m, (CAST)IPSECDIFF(f), plural(IPSECDIFF(f))) +#define hist(f, n, t) \ + ipsec_hist(ipsecstat.f, pipsecstat.f, \ + sizeof(ipsecstat.f)/sizeof(ipsecstat.f[0]), (n), (t)); + + if (interval && vflag > 0) + print_time(); + + p(in_success, "\t" LLU " inbound packet%s processed successfully\n"); + p(in_polvio, "\t" LLU " inbound packet%s violated process security " + "policy\n"); + p(in_nosa, "\t" LLU " inbound packet%s with no SA available\n"); + p(in_inval, "\t" LLU " invalid inbound packet%s\n"); + p(in_nomem, "\t" LLU " inbound packet%s failed due to insufficient memory\n"); + p(in_badspi, "\t" LLU " inbound packet%s failed getting SPI\n"); + p(in_ahreplay, "\t" LLU " inbound packet%s failed on AH replay check\n"); + p(in_espreplay, "\t" LLU " inbound packet%s failed on ESP replay check\n"); + p(in_ahauthsucc, "\t" LLU " inbound packet%s considered authentic\n"); + p(in_ahauthfail, "\t" LLU " inbound packet%s failed on authentication\n"); + hist(in_ahhist, ipsec_ahnames, "AH input"); + hist(in_esphist, ipsec_espnames, "ESP input"); + hist(in_comphist, ipsec_compnames, "IPComp input"); + + p(out_success, "\t" LLU " outbound packet%s processed successfully\n"); + p(out_polvio, "\t" LLU " outbound packet%s violated process security " + "policy\n"); + p(out_nosa, "\t" LLU " outbound packet%s with no SA available\n"); + p(out_inval, "\t" LLU " invalid outbound packet%s\n"); + p(out_nomem, "\t" LLU " outbound packet%s failed due to insufficient memory\n"); + p(out_noroute, "\t" LLU " outbound packet%s with no route\n"); + hist(out_ahhist, ipsec_ahnames, "AH output"); + hist(out_esphist, ipsec_espnames, "ESP output"); + hist(out_comphist, ipsec_compnames, "IPComp output"); +#undef IPSECDIFF +#undef p +#undef hist +} + +void +ipsec_stats(uint32_t off __unused, char *name, int af __unused) +{ + size_t len; + + len = sizeof(struct ipsecstat); + if (strcmp(name, "ipsec") == 0) { + if (sysctlbyname("net.inet.ipsec.stats", &ipsecstat, &len, 0, 0) == -1) + return; + } else if (strcmp(name, "ipsec6") == 0) { + if (sysctlbyname("net.inet6.ipsec6.stats", &ipsecstat, &len, 0, 0) == -1) + return; + } else + return; + printf ("%s:\n", name); + + print_ipsecstats(); + + if (interval > 0) + bcopy(&ipsecstat, &pipsecstat, len); +} + +static const char * +pfkey_msgtype_names(int x) +{ + const int max = + sizeof(pfkey_msgtypenames)/sizeof(pfkey_msgtypenames[0]); + static char buf[10]; + + if (x < max && pfkey_msgtypenames[x]) + return pfkey_msgtypenames[x]; + snprintf(buf, sizeof(buf), "#%d", x); + return buf; +} + +void +pfkey_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct pfkeystat ppfkeystat; + struct pfkeystat pfkeystat; + unsigned first, type; + size_t len; + + len = sizeof(struct pfkeystat); + if (sysctlbyname("net.key.pfkeystat", &pfkeystat, &len, 0, 0) == -1) + return; + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define PFKEYDIFF(f) (pfkeystat.f - ppfkeystat.f) +#define p(f, m) if (PFKEYDIFF(f) || sflag <= 1) \ + printf(m, (CAST)PFKEYDIFF(f), plural(PFKEYDIFF(f))) + + /* kernel -> userland */ + p(out_total, "\t" LLU " request%s sent to userland\n"); + p(out_bytes, "\t" LLU " byte%s sent to userland\n"); + for (first = 1, type = 0; + type < sizeof(pfkeystat.out_msgtype)/sizeof(pfkeystat.out_msgtype[0]); + type++) { + if (PFKEYDIFF(out_msgtype[type]) <= 0) + continue; + if (first) { + printf("\thistogram by message type:\n"); + first = 0; + } + printf("\t\t%s: " LLU "\n", pfkey_msgtype_names(type), + (CAST)PFKEYDIFF(out_msgtype[type])); + } + p(out_invlen, "\t" LLU " message%s with invalid length field\n"); + p(out_invver, "\t" LLU " message%s with invalid version field\n"); + p(out_invmsgtype, "\t" LLU " message%s with invalid message type field\n"); + p(out_tooshort, "\t" LLU " message%s too short\n"); + p(out_nomem, "\t" LLU " message%s with memory allocation failure\n"); + p(out_dupext, "\t" LLU " message%s with duplicate extension\n"); + p(out_invexttype, "\t" LLU " message%s with invalid extension type\n"); + p(out_invsatype, "\t" LLU " message%s with invalid sa type\n"); + p(out_invaddr, "\t" LLU " message%s with invalid address extension\n"); + + /* userland -> kernel */ + p(in_total, "\t" LLU " request%s sent from userland\n"); + p(in_bytes, "\t" LLU " byte%s sent from userland\n"); + for (first = 1, type = 0; + type < sizeof(pfkeystat.in_msgtype)/sizeof(pfkeystat.in_msgtype[0]); + type++) { + if (PFKEYDIFF(in_msgtype[type]) <= 0) + continue; + if (first) { + printf("\thistogram by message type:\n"); + first = 0; + } + printf("\t\t%s: " LLU "\n", pfkey_msgtype_names(type), + (CAST)PFKEYDIFF(in_msgtype[type])); + } + p(in_msgtarget[KEY_SENDUP_ONE], + "\t" LLU " message%s toward single socket\n"); + p(in_msgtarget[KEY_SENDUP_ALL], + "\t" LLU " message%s toward all sockets\n"); + p(in_msgtarget[KEY_SENDUP_REGISTERED], + "\t" LLU " message%s toward registered sockets\n"); + p(in_nomem, "\t" LLU " message%s with memory allocation failure\n"); + + if (interval > 0) + bcopy(&pfkeystat, &ppfkeystat, len); +#undef PFKEYDIFF +#undef p +} +#endif /*IPSEC*/ diff --git a/network_cmds/netstat.tproj/main.c b/network_cmds/netstat.tproj/main.c new file mode 100644 index 0000000..1b8381e --- /dev/null +++ b/network_cmds/netstat.tproj/main.c @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2008-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1988, 1993 + * Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char const copyright[] = +"@(#) Copyright (c) 1983, 1988, 1993\n\ + Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/sys_domain.h> + +#include <netinet/in.h> +#include <net/pfkeyv2.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" +#include <sys/types.h> +#include <sys/sysctl.h> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $Id: main.c,v 1.8 2004/10/14 22:24:09 lindak Exp $ + * + */ + +struct protox { + void (*pr_cblocks)(uint32_t, char *, int); + /* control blocks printing routine */ + void (*pr_stats)(uint32_t, char *, int); + /* statistics printing routine */ + void (*pr_istats)(char *); /* per/if statistics printing routine */ + char *pr_name; /* well-known name */ + int pr_protocol; +} protox[] = { + { protopr, tcp_stats, NULL, "tcp", IPPROTO_TCP }, + { protopr, udp_stats, NULL, "udp", IPPROTO_UDP }, + { protopr, NULL, NULL, "divert", IPPROTO_DIVERT }, + { protopr, ip_stats, NULL, "ip", IPPROTO_RAW }, + { protopr, icmp_stats, NULL, "icmp", IPPROTO_ICMP }, + { protopr, igmp_stats, NULL, "igmp", IPPROTO_IGMP }, +#ifdef IPSEC + { NULL, ipsec_stats, NULL, "ipsec", IPPROTO_ESP}, +#endif + { NULL, arp_stats, NULL, "arp", 0 }, + { mptcppr, mptcp_stats, NULL, "mptcp", IPPROTO_TCP }, + { NULL, NULL, NULL, NULL, 0 } +}; + +#ifdef INET6 +struct protox ip6protox[] = { + { protopr, tcp_stats, NULL, "tcp", IPPROTO_TCP }, + { protopr, udp_stats, NULL, "udp", IPPROTO_UDP }, + { protopr, ip6_stats, ip6_ifstats, "ip6", IPPROTO_RAW }, + { protopr, icmp6_stats, icmp6_ifstats, "icmp6",IPPROTO_ICMPV6 }, +#ifdef IPSEC + { NULL, ipsec_stats, NULL, "ipsec6", IPPROTO_ESP }, +#endif + { NULL, rip6_stats, NULL, "rip6", IPPROTO_RAW }, + { mptcppr, mptcp_stats, NULL, "mptcp", IPPROTO_TCP }, + { NULL, NULL, NULL, NULL, 0 } +}; +#endif /*INET6*/ + +#ifdef IPSEC +struct protox pfkeyprotox[] = { + { NULL, pfkey_stats, NULL, "pfkey", PF_KEY_V2 }, + { NULL, NULL, NULL, NULL, 0 } +}; +#endif + + +struct protox systmprotox[] = { + { systmpr, NULL, NULL, "reg", 0 }, + { systmpr, kevt_stats, NULL, "kevt", SYSPROTO_EVENT }, + { systmpr, kctl_stats, NULL, "kctl", SYSPROTO_CONTROL }, + { NULL, NULL, NULL, NULL, 0 } +}; + +struct protox nstatprotox[] = { + { NULL, print_nstat_stats, NULL, "nstat", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +struct protox ipcprotox[] = { + { NULL, print_extbkidle_stats, NULL, "xbkidle", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +struct protox kernprotox[] = { + { NULL, print_net_api_stats, NULL, "net_api", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +#ifdef AF_VSOCK +struct protox vsockprotox[] = { + { vsockpr, NULL, NULL, "vsock", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; +#endif + +struct protox *protoprotox[] = { + protox, +#ifdef INET6 + ip6protox, +#endif +#ifdef IPSEC + pfkeyprotox, +#endif + systmprotox, + nstatprotox, + ipcprotox, + kernprotox, +#ifdef AF_VSOCK + vsockprotox, +#endif + NULL +}; + +static void printproto (struct protox *, char *); +static void usage (void); +static struct protox *name2protox (char *); +static struct protox *knownname (char *); +#ifdef SRVCACHE +extern void _serv_cache_close(); +#endif + +int Aflag; /* show addresses of protocol control block */ +int aflag; /* show all sockets (including servers) */ +int bflag; /* show i/f total bytes in/out */ +int cflag; /* show specific classq */ +int dflag; /* show i/f dropped packets */ +int Fflag; /* show i/f forwarded packets */ +#if defined(__APPLE__) +int gflag; /* show group (multicast) routing or stats */ +#endif +int iflag; /* show interfaces */ +int lflag; /* show routing table with more information */ +int Lflag; /* show size of listen queues */ +int mflag; /* show memory stats */ +int nflag; /* show addresses numerically */ +static int pflag; /* show given protocol */ +int prioflag = -1; /* show packet priority statistics */ +int Rflag; /* show reachability information */ +int rflag; /* show routing tables (or routing stats) */ +int sflag; /* show protocol statistics */ +int Sflag; /* show additional i/f link status */ +int tflag; /* show i/f watchdog timers */ +int vflag; /* more verbose */ +int Wflag; /* wide display */ +int qflag; /* classq stats display */ +int Qflag; /* opportunistic polling stats display */ +int xflag; /* show extended link-layer reachability information */ +int zflag; /* show only entries with non zero rtt metrics */ + +int cq = -1; /* send classq index (-1 for all) */ +int interval; /* repeat interval for i/f stats */ + +char *interface; /* desired i/f for stats, or NULL for all i/fs */ +int unit; /* unit number for above */ + +int af; /* address family */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct protox *tp = NULL; /* for printing cblocks & stats */ + int ch; + + af = AF_UNSPEC; + + while ((ch = getopt(argc, argv, "Aabc:dFf:gI:ikLlmnP:p:qQrRsStuvWw:xz")) != -1) + switch(ch) { + case 'A': + Aflag = 1; + break; + case 'a': + aflag = 1; + break; + case 'b': + bflag = 1; + break; + case 'c': + cflag = 1; + cq = atoi(optarg); + break; + case 'd': + dflag = 1; + break; + case 'F': + Fflag = 1; + break; + case 'f': + if (strcmp(optarg, "ipx") == 0) + af = AF_IPX; + else if (strcmp(optarg, "inet") == 0) + af = AF_INET; +#ifdef INET6 + else if (strcmp(optarg, "inet6") == 0) + af = AF_INET6; +#endif /*INET6*/ +#ifdef INET6 + else if (strcmp(optarg, "pfkey") == 0) + af = PF_KEY; +#endif /*INET6*/ + else if (strcmp(optarg, "unix") == 0) + af = AF_UNIX; + else if (strcmp(optarg, "systm") == 0) + af = AF_SYSTEM; + else { + errx(1, "%s: unknown address family", optarg); + } + break; +#if defined(__APPLE__) + case 'g': + gflag = 1; + break; +#endif + case 'I': { + char *cp; + + iflag = 1; + for (cp = interface = optarg; isalpha(*cp); cp++) + continue; + unit = atoi(cp); + break; + } + case 'i': + iflag = 1; + break; + case 'l': + lflag += 1; + break; + case 'L': + Lflag = 1; + break; + case 'm': + mflag++; + break; + case 'n': + nflag = 1; + break; + case 'P': + prioflag = atoi(optarg); + break; + case 'p': + if ((tp = name2protox(optarg)) == NULL) { + errx(1, + "%s: unknown or uninstrumented protocol", + optarg); + } + pflag = 1; + break; + case 'q': + qflag++; + break; + case 'Q': + Qflag++; + break; + case 'R': + Rflag = 1; + break; + case 'r': + rflag = 1; + break; + case 's': + ++sflag; + break; + case 'S': + Sflag = 1; + break; + case 't': + tflag = 1; + break; + case 'u': + af = AF_UNIX; + break; + case 'v': + vflag++; + break; + case 'W': + Wflag = 1; + break; + case 'w': + interval = atoi(optarg); + iflag = 1; + break; + case 'x': + xflag = 1; + Rflag = 1; + break; + case 'z': + zflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + if (isdigit(**argv)) { + interval = atoi(*argv); + if (interval <= 0) + usage(); + ++argv; + iflag = 1; + } + } +#endif + + if (mflag) { + mbpr(); + exit(0); + } + if (iflag && !sflag && !Sflag && !gflag && !qflag && !Qflag) { + if (Rflag) + intpr_ri(NULL); + else + intpr(NULL); + exit(0); + } + if (rflag) { + if (sflag) + rt_stats(); + else + routepr(); + exit(0); + } + if (qflag || Qflag) { + if (interface == NULL) { + fprintf(stderr, "%s statistics option " + "requires interface name\n", qflag ? "Queue" : + "Polling"); + } else if (qflag) { + aqstatpr(); + } else { + rxpollstatpr(); + } + exit(0); + } + if (Sflag) { + if (interface == NULL) { + fprintf(stderr, "additional link status option" + " requires interface name\n"); + } else { + print_link_status(interface); + } + exit(0); + } + +#if defined(__APPLE__) + if (gflag) { + ifmalist_dump(); + exit(0); + } +#endif + + if (tp) { + printproto(tp, tp->pr_name); + exit(0); + } + if (af == AF_INET || af == AF_UNSPEC) + for (tp = protox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#ifdef INET6 + if (af == AF_INET6 || af == AF_UNSPEC) + for (tp = ip6protox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /*INET6*/ +#ifdef IPSEC + if (af == PF_KEY || af == AF_UNSPEC) + for (tp = pfkeyprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /*IPSEC*/ + if ((af == AF_UNIX || af == AF_UNSPEC) && !Lflag && !sflag) + unixpr(); + + if ((af == AF_SYSTEM || af == AF_UNSPEC) && !Lflag) + for (tp = systmprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#if TARGET_OS_IPHONE + if (af == AF_UNSPEC && !Lflag) + for (tp = nstatprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /* TARGET_OS_IPHONE */ + + if (af == AF_UNSPEC && !Lflag) + for (tp = ipcprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); + + if (af == AF_UNSPEC && !Lflag) + for (tp = kernprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); + +#ifdef AF_VSOCK + if (af == AF_VSOCK || af == AF_UNSPEC) + for (tp = vsockprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /*AF_VSOCK*/ + +#ifdef SRVCACHE + _serv_cache_close(); +#endif + exit(0); +} + +/* + * Print out protocol statistics or control blocks (per sflag). + * If the interface was not specifically requested, and the symbol + * is not in the namelist, ignore this one. + */ +static void +printproto(tp, name) + register struct protox *tp; + char *name; +{ + void (*pr)(uint32_t, char *, int); + uint32_t off; + + if (sflag) { + if (iflag && !pflag) { + if (tp->pr_istats) + intpr(tp->pr_istats); + else if (vflag) + printf("%s: no per-interface stats routine\n", + tp->pr_name); + return; + } + else { + pr = tp->pr_stats; + if (!pr) { + if (pflag && vflag) + printf("%s: no stats routine\n", + tp->pr_name); + return; + } + off = tp->pr_protocol; + } + } else { + pr = tp->pr_cblocks; + if (!pr) { + if (pflag && vflag) + printf("%s: no PCB routine\n", tp->pr_name); + return; + } + off = tp->pr_protocol; + } + if (pr != NULL) { + if (sflag && iflag && pflag) + intervalpr(pr, off, name, af); + else + (*pr)(off, name, af); + } else { + printf("### no stats for %s\n", name); + } +} + +char * +plural(int n) +{ + return (n > 1 ? "s" : ""); +} + +char * +plurales(int n) +{ + return (n > 1 ? "es" : ""); +} + +char * +pluralies(int n) +{ + return (n > 1 ? "ies" : "y"); +} + +/* + * Find the protox for the given "well-known" name. + */ +static struct protox * +knownname(char *name) +{ + struct protox **tpp, *tp; + + for (tpp = protoprotox; *tpp; tpp++) + for (tp = *tpp; tp->pr_name; tp++) + if (strcmp(tp->pr_name, name) == 0) + return (tp); + return (NULL); +} + +/* + * Find the protox corresponding to name. + */ +static struct protox * +name2protox(char *name) +{ + struct protox *tp; + char **alias; /* alias from p->aliases */ + struct protoent *p; + + /* + * Try to find the name in the list of "well-known" names. If that + * fails, check if name is an alias for an Internet protocol. + */ + if ((tp = knownname(name)) != NULL) + return (tp); + + setprotoent(1); /* make protocol lookup cheaper */ + while ((p = getprotoent()) != NULL) { + /* assert: name not same as p->name */ + for (alias = p->p_aliases; *alias; alias++) + if (strcmp(name, *alias) == 0) { + endprotoent(); + return (knownname(p->p_name)); + } + } + endprotoent(); + return (NULL); +} + +#define NETSTAT_USAGE "\ +Usage: netstat [-AaLlnW] [-f address_family | -p protocol]\n\ + netstat [-gilns] [-f address_family]\n\ + netstat -i | -I interface [-w wait] [-abdgRtS]\n\ + netstat -s [-s] [-f address_family | -p protocol] [-w wait]\n\ + netstat -i | -I interface -s [-f address_family | -p protocol]\n\ + netstat -m [-m]\n\ + netstat -r [-Aaln] [-f address_family]\n\ + netstat -rs [-s]\n\ +" + +static void +usage(void) +{ + (void) fprintf(stderr, "%s\n", NETSTAT_USAGE); + exit(1); +} + +int +print_time(void) +{ + time_t now; + struct tm tm; + int num_written = 0; + + (void) time(&now); + (void) localtime_r(&now, &tm); + + num_written += printf("%02d:%02d:%02d ", tm.tm_hour, tm.tm_min, tm.tm_sec); + + return (num_written); +} + diff --git a/network_cmds/netstat.tproj/mbuf.c b/network_cmds/netstat.tproj/mbuf.c new file mode 100644 index 0000000..38645e9 --- /dev/null +++ b/network_cmds/netstat.tproj/mbuf.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include "netstat.h" + +#define YES 1 +typedef int bool; + +struct mbstat mbstat; + +static struct mbtypes { + int mt_type; + char *mt_name; +} mbtypes[] = { + { MT_DATA, "data" }, + { MT_OOBDATA, "oob data" }, + { MT_CONTROL, "ancillary data" }, + { MT_HEADER, "packet headers" }, + { MT_SOCKET, "socket structures" }, /* XXX */ + { MT_PCB, "protocol control blocks" }, /* XXX */ + { MT_RTABLE, "routing table entries" }, /* XXX */ + { MT_HTABLE, "IMP host table entries" }, /* XXX */ + { MT_ATABLE, "address resolution tables" }, + { MT_FTABLE, "fragment reassembly queue headers" }, /* XXX */ + { MT_SONAME, "socket names and addresses" }, + { MT_SOOPTS, "socket options" }, + { MT_RIGHTS, "access rights" }, + { MT_IFADDR, "interface addresses" }, /* XXX */ + { MT_TAG, "packet tags" }, /* XXX */ + { 0, 0 } +}; + +int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short); +bool seen[256]; /* "have we seen this type yet?" */ + +mb_stat_t *mb_stat; +unsigned int njcl, njclbytes; +mleak_stat_t *mleak_stat; +struct mleak_table table; + +#define KERN_IPC_MB_STAT "kern.ipc.mb_stat" +#define KERN_IPC_NJCL "kern.ipc.njcl" +#define KERN_IPC_NJCL_BYTES "kern.ipc.njclbytes" +#define KERN_IPC_MLEAK_TABLE "kern.ipc.mleak_table" +#define KERN_IPC_MLEAK_TOP_TRACE "kern.ipc.mleak_top_trace" + +#define MB_STAT_HDR1 "\ +class buf active ctotal total cache cached uncached memory\n\ +name size bufs bufs bufs state bufs bufs usage\n\ +---------- ----- -------- -------- -------- ----- -------- -------- ---------\n\ +" + +#define MB_STAT_HDR2 "\n\ +class waiter notify purge wretry nwretry failure\n\ +name count count count count count count\n\ +---------- -------- -------- -------- -------- -------- --------\n\ +" + +#define MB_LEAK_HDR "\n\ + calltrace [1] calltrace [2] calltrace [3] calltrace [4] calltrace [5] \n\ + ------------------ ------------------ ------------------ ------------------ ------------------ \n\ +" + +#define MB_LEAK_SPACING " " +static const char *mbpr_state(int); +static const char *mbpr_mem(u_int32_t); +static int mbpr_getdata(void); + +/* + * Print mbuf statistics. + */ +void +mbpr(void) +{ + unsigned long totmem = 0, totfree = 0, totmbufs, totused, totreturned = 0; + double totpct; + u_int32_t m_msize, m_mbufs = 0, m_clfree = 0, m_bigclfree = 0; + u_int32_t m_mbufclfree = 0, m_mbufbigclfree = 0; + u_int32_t m_16kclusters = 0, m_16kclfree = 0, m_mbuf16kclfree = 0; + int i; + struct mbtypes *mp; + mb_class_stat_t *cp; + + if (mbpr_getdata() != 0) + return; + + m_msize = mbstat.m_msize; + cp = &mb_stat->mbs_class[0]; + for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { + if (cp->mbcl_size == m_msize) { + m_mbufs = cp->mbcl_active; + } else if (cp->mbcl_size == mbstat.m_mclbytes) { + m_clfree = cp->mbcl_total - cp->mbcl_active; + } else if (cp->mbcl_size == mbstat.m_bigmclbytes) { + m_bigclfree = cp->mbcl_total - cp->mbcl_active; + } else if (njcl > 0 && cp->mbcl_size == njclbytes) { + m_16kclfree = cp->mbcl_total - cp->mbcl_active; + m_16kclusters = cp->mbcl_total; + } else if (cp->mbcl_size == (m_msize + mbstat.m_mclbytes)) { + m_mbufclfree = cp->mbcl_total - cp->mbcl_active; + } else if (cp->mbcl_size == (m_msize + mbstat.m_bigmclbytes)) { + m_mbufbigclfree = cp->mbcl_total - cp->mbcl_active; + } else if (njcl > 0 && cp->mbcl_size == (m_msize + njclbytes)) { + m_mbuf16kclfree = cp->mbcl_total - cp->mbcl_active; + } + } + + /* adjust free counts to include composite caches */ + m_clfree += m_mbufclfree; + m_bigclfree += m_mbufbigclfree; + m_16kclfree += m_mbuf16kclfree; + + cp = &mb_stat->mbs_class[0]; + for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { + u_int32_t mem; + + mem = cp->mbcl_ctotal * cp->mbcl_size; + totmem += mem; + totreturned += cp->mbcl_release_cnt; + totfree += (cp->mbcl_mc_cached + cp->mbcl_infree) * + cp->mbcl_size; + if (mflag > 1) { + if (i == 0) + printf(MB_STAT_HDR1); + + if (njcl == 0 && + cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes)) + continue; + + printf("%-10s %5u %8u %8u %8u %5s %8u %8u %9s\n", + cp->mbcl_cname, cp->mbcl_size, cp->mbcl_active, + cp->mbcl_ctotal, cp->mbcl_total, + mbpr_state(cp->mbcl_mc_state), cp->mbcl_mc_cached, + cp->mbcl_infree, mbpr_mem(mem)); + } + } + + cp = &mb_stat->mbs_class[0]; + for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { + if (mflag > 2) { + if (i == 0) + printf(MB_STAT_HDR2); + + if (njcl == 0 && + cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes)) + continue; + + printf("%-10s %8u %8llu %8llu %8u %8u %8llu\n", + cp->mbcl_cname, cp->mbcl_mc_waiter_cnt, + cp->mbcl_notified, cp->mbcl_purge_cnt, + cp->mbcl_mc_wretry_cnt, cp->mbcl_mc_nwretry_cnt, + cp->mbcl_fail_cnt); + } + } + + if (mflag > 1) + printf("\n"); + + totmbufs = 0; + for (mp = mbtypes; mp->mt_name; mp++) + totmbufs += mbstat.m_mtypes[mp->mt_type]; + /* + * These stats are not updated atomically in the kernel; + * adjust the total as neeeded. + */ + if (totmbufs > m_mbufs) + totmbufs = m_mbufs; + printf("%lu/%u mbufs in use:\n", totmbufs, m_mbufs); + for (mp = mbtypes; mp->mt_name; mp++) + if (mbstat.m_mtypes[mp->mt_type]) { + seen[mp->mt_type] = YES; + printf("\t%u mbufs allocated to %s\n", + mbstat.m_mtypes[mp->mt_type], mp->mt_name); + } + seen[MT_FREE] = YES; + for (i = 0; i < nmbtypes; i++) + if (!seen[i] && mbstat.m_mtypes[i]) { + printf("\t%u mbufs allocated to <mbuf type %d>\n", + mbstat.m_mtypes[i], i); + } + if ((m_mbufs - totmbufs) > 0) + printf("\t%lu mbufs allocated to caches\n", + m_mbufs - totmbufs); + printf("%u/%u mbuf 2KB clusters in use\n", + (unsigned int)(mbstat.m_clusters - m_clfree), + (unsigned int)mbstat.m_clusters); + printf("%u/%u mbuf 4KB clusters in use\n", + (unsigned int)(mbstat.m_bigclusters - m_bigclfree), + (unsigned int)mbstat.m_bigclusters); + if (njcl > 0) { + printf("%u/%u mbuf %uKB clusters in use\n", + m_16kclusters - m_16kclfree, m_16kclusters, + njclbytes/1024); + } + totused = totmem - totfree; + if (totmem == 0) + totpct = 0; + else if (totused < (ULONG_MAX/100)) + totpct = (totused * 100)/(double)totmem; + else { + u_long totmem1 = totmem/100; + u_long totused1 = totused/100; + totpct = (totused1 * 100)/(double)totmem1; + } + printf("%lu KB allocated to network (%.1f%% in use)\n", + totmem / 1024, totpct); + printf("%lu KB returned to the system\n", totreturned / 1024); + + printf("%u requests for memory denied\n", (unsigned int)mbstat.m_drops); + printf("%u requests for memory delayed\n", (unsigned int)mbstat.m_wait); + printf("%u calls to drain routines\n", (unsigned int)mbstat.m_drain); + + free(mb_stat); + mb_stat = NULL; + + if (mleak_stat != NULL) { + mleak_trace_stat_t *mltr; + + printf("\nmbuf leak detection table:\n"); + printf("\ttotal captured: %u (one per %u)\n" + "\ttotal allocs outstanding: %llu\n" + "\tnew hash recorded: %llu allocs, %llu traces\n" + "\thash collisions: %llu allocs, %llu traces\n" + "\toverwrites: %llu allocs, %llu traces\n" + "\tlock conflicts: %llu\n\n", + table.mleak_capture / table.mleak_sample_factor, + table.mleak_sample_factor, + table.outstanding_allocs, + table.alloc_recorded, table.trace_recorded, + table.alloc_collisions, table.trace_collisions, + table.alloc_overwrites, table.trace_overwrites, + table.total_conflicts); + + printf("top %d outstanding traces:\n", mleak_stat->ml_cnt); + for (i = 0; i < mleak_stat->ml_cnt; i++) { + mltr = &mleak_stat->ml_trace[i]; + printf("[%d] %llu outstanding alloc(s), " + "%llu hit(s), %llu collision(s)\n", (i + 1), + mltr->mltr_allocs, mltr->mltr_hitcount, + mltr->mltr_collisions); + } + + printf(MB_LEAK_HDR); + for (i = 0; i < MLEAK_STACK_DEPTH; i++) { + int j; + + printf("%2d: ", (i + 1)); + for (j = 0; j < mleak_stat->ml_cnt; j++) { + mltr = &mleak_stat->ml_trace[j]; + if (i < mltr->mltr_depth) { + if (mleak_stat->ml_isaddr64) { + printf("0x%0llx ", + mltr->mltr_addr[i]); + } else { + printf("0x%08x ", + (u_int32_t)mltr->mltr_addr[i]); + } + } else { + printf(MB_LEAK_SPACING); + } + } + printf("\n"); + } + free(mleak_stat); + mleak_stat = NULL; + } +} + +static const char * +mbpr_state(int state) +{ + char *msg = "?"; + + switch (state) { + case MCS_DISABLED: + msg = "dis"; + break; + + case MCS_ONLINE: + msg = "on"; + break; + + case MCS_PURGING: + msg = "purge"; + break; + + case MCS_OFFLINE: + msg = "off"; + break; + } + return (msg); +} + +static const char * +mbpr_mem(u_int32_t bytes) +{ + static char buf[33]; + double mem = bytes; + + if (mem < 1024) { + (void) snprintf(buf, sizeof (buf), "%d", (int)mem); + } else if ((mem /= 1024) < 1024) { + (void) snprintf(buf, sizeof (buf), "%.1f KB", mem); + } else { + mem /= 1024; + (void) snprintf(buf, sizeof (buf), "%.1f MB", mem); + } + return (buf); +} + +static int +mbpr_getdata(void) +{ + size_t len; + int error = -1; + + if (nmbtypes != 256) { + (void) fprintf(stderr, + "netstat: unexpected change to mbstat; check source\n"); + goto done; + } + + len = sizeof(mbstat); + if (sysctlbyname("kern.ipc.mbstat", &mbstat, &len, 0, 0) == -1) + goto done; + + if (sysctlbyname(KERN_IPC_MB_STAT, NULL, &len, 0, 0) == -1) { + (void) fprintf(stderr, + "Error retrieving length for %s\n", KERN_IPC_MB_STAT); + goto done; + } + + mb_stat = calloc(1, len); + if (mb_stat == NULL) { + (void) fprintf(stderr, + "Error allocating %lu bytes for sysctl data\n", len); + goto done; + } + + if (sysctlbyname(KERN_IPC_MB_STAT, mb_stat, &len, 0, 0) == -1) { + (void) fprintf(stderr, + "Error %d getting %s\n", errno, KERN_IPC_MB_STAT); + goto done; + } + + if (mb_stat->mbs_cnt == 0) { + (void) fprintf(stderr, + "Invalid mbuf class count (%d)\n", mb_stat->mbs_cnt); + goto done; + } + + /* mbuf leak detection! */ + if (mflag > 3) { + errno = 0; + len = sizeof (table); + if (sysctlbyname(KERN_IPC_MLEAK_TABLE, &table, &len, 0, 0) == + -1 && errno != ENXIO) { + (void) fprintf(stderr, "error %d getting %s\n", errno, + KERN_IPC_MLEAK_TABLE); + goto done; + } else if (errno == ENXIO) { + (void) fprintf(stderr, "mbuf leak detection is not " + "enabled in the kernel.\n"); + goto skip; + } + + if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, NULL, &len, + 0, 0) == -1) { + (void) fprintf(stderr, "Error retrieving length for " + "%s: %d\n", KERN_IPC_MB_STAT, errno); + goto done; + } + + mleak_stat = calloc(1, len); + if (mleak_stat == NULL) { + (void) fprintf(stderr, "Error allocating %lu bytes " + "for sysctl data\n", len); + goto done; + } + + if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, mleak_stat, &len, + 0, 0) == -1) { + (void) fprintf(stderr, "error %d getting %s\n", errno, + KERN_IPC_MLEAK_TOP_TRACE); + goto done; + } + } + +skip: + len = sizeof (njcl); + (void) sysctlbyname(KERN_IPC_NJCL, &njcl, &len, 0, 0); + len = sizeof (njclbytes); + (void) sysctlbyname(KERN_IPC_NJCL_BYTES, &njclbytes, &len, 0, 0); + + error = 0; + +done: + if (error != 0 && mb_stat != NULL) { + free(mb_stat); + mb_stat = NULL; + } + + if (error != 0 && mleak_stat != NULL) { + free(mleak_stat); + mleak_stat = NULL; + } + + return (error); +} diff --git a/network_cmds/netstat.tproj/mcast.c b/network_cmds/netstat.tproj/mcast.c new file mode 100644 index 0000000..3637f4d --- /dev/null +++ b/network_cmds/netstat.tproj/mcast.c @@ -0,0 +1,887 @@ +/* + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> + +/* + * Print the running system's current multicast group memberships. + * As this relies on getifmaddrs(), it may not be used with a core file. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_mib.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <netinet/igmp_var.h> +#include <netinet6/mld6_var.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <ctype.h> +#include <err.h> +#include <ifaddrs.h> +#include <sysexits.h> + +#include <stddef.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <ifaddrs.h> + + +#include "netstat.h" + +union sockunion { + struct sockaddr_storage ss; + struct sockaddr sa; + struct sockaddr_dl sdl; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +typedef union sockunion sockunion_t; + +/* + * This may have been defined in <net/if.h>. Note that if <net/if.h> is + * to be included it must be included before this header file. + */ +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif + +//struct ifmaddrs { +// struct ifmaddrs *ifma_next; +// struct sockaddr *ifma_name; +// struct sockaddr *ifma_addr; +// struct sockaddr *ifma_lladdr; +//}; + +void ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af); +static int ifmalist_dump_mcstat(struct ifmaddrs *); +static void in_ifinfo(struct igmp_ifinfo *); +static const char *inm_mode(u_int); +static void inm_print_sources_sysctl(uint32_t, struct in_addr); +#ifdef INET6 +static void in6_ifinfo(struct mld_ifinfo *); +static void in6m_print_sources_sysctl(uint32_t, struct in6_addr *); +static const char *inet6_n2a(struct in6_addr *); +#endif +static void printb(const char *, unsigned int, const char *); +static const char *sdl_addr_to_hex(const struct sockaddr_dl *, char *, int); + +extern char *routename6(struct sockaddr_in6 *); + +#define sa_equal(a1, a2) \ + (bcmp((a1), (a2), ((a1))->sa_len) == 0) + +#define sa_dl_equal(a1, a2) \ + ((((struct sockaddr_dl *)(a1))->sdl_len == \ + ((struct sockaddr_dl *)(a2))->sdl_len) && \ + (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \ + LLADDR((struct sockaddr_dl *)(a2)), \ + ((struct sockaddr_dl *)(a1))->sdl_alen) == 0)) + +#define SALIGN (sizeof(uint32_t) - 1) +#define SA_RLEN(sa) (sa ? ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ + (SALIGN + 1)) : 0) +#define MAX_SYSCTL_TRY 5 +#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) + +void +ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af) +{ + const struct ifmaddrs *ifma; + sockunion_t *psa; + char myifname[IFNAMSIZ]; + char *pcolon; + char *pafname, *pifname, *plladdr = NULL, *pgroup = NULL; + + switch (af) { + case AF_INET: + pafname = "IPv4"; + break; +#ifdef INET6 + case AF_INET6: + pafname = "IPv6"; + break; +#endif + case AF_LINK: + pafname = "Link-layer"; + break; + default: + return; /* XXX */ + } + + fprintf(stdout, "%s Multicast Group Memberships\n", pafname); + fprintf(stdout, "%-20s\t%-16s\t%s\n", "Group", "Link-layer Address", + "Netif"); + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + + /* Group address */ + psa = (sockunion_t *)ifma->ifma_addr; + if (psa->sa.sa_family != af) + continue; + + switch (psa->sa.sa_family) { + case AF_INET: + pgroup = inet_ntoa(psa->sin.sin_addr); + break; +#ifdef INET6 + case AF_INET6: + pgroup = routename6(&(psa->sin6)); + break; +#endif + case AF_LINK: + if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) || + (psa->sdl.sdl_type == IFT_ETHER)) { + pgroup = +ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data); +#ifdef notyet + } else { + pgroup = addr2ascii(AF_LINK, + &psa->sdl, + sizeof(struct sockaddr_dl), + addrbuf); +#endif + } + break; + default: + continue; /* XXX */ + } + + /* Link-layer mapping, if any */ + psa = (sockunion_t *)ifma->ifma_lladdr; + if (psa != NULL) { + if (psa->sa.sa_family == AF_LINK) { + if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) || + (psa->sdl.sdl_type == IFT_ETHER)) { + /* IEEE 802 */ + plladdr = +ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data); +#ifdef notyet + } else { + /* something more exotic */ + plladdr = addr2ascii(AF_LINK, + &psa->sdl, + sizeof(struct sockaddr_dl), + addrbuf); +#endif + } + } else { + int i; + + /* not a link-layer address */ + plladdr = "<invalid>"; + + for (i = 0; psa->sa.sa_len > 2 && i < psa->sa.sa_len - 2; i++) + printf("0x%x ", psa->sa.sa_data[i]); + printf("\n"); + } + } else { + plladdr = "<none>"; + } + + /* Interface upon which the membership exists */ + psa = (sockunion_t *)ifma->ifma_name; + if (psa != NULL && psa->sa.sa_family == AF_LINK) { + strlcpy(myifname, link_ntoa(&psa->sdl), sizeof(myifname)); + pcolon = strchr(myifname, ':'); + if (pcolon) + *pcolon = '\0'; + pifname = myifname; + } else { + pifname = ""; + } + + fprintf(stdout, "%-20s\t%-16s\t%s\n", pgroup, plladdr, pifname); + } +} + +void +ifmalist_dump(void) +{ + struct ifmaddrs *ifmap; + + if (getifmaddrs(&ifmap)) + err(EX_OSERR, "getifmaddrs"); + + ifmalist_dump_af(ifmap, AF_LINK); + fputs("\n", stdout); + ifmalist_dump_af(ifmap, AF_INET); +#ifdef INET6 + fputs("\n", stdout); + ifmalist_dump_af(ifmap, AF_INET6); +#endif + if (sflag) { + fputs("\n", stdout); + ifmalist_dump_mcstat(ifmap); + } + + freeifmaddrs(ifmap); +} + +static int +ifmalist_dump_mcstat(struct ifmaddrs *ifmap) +{ + char thisifname[IFNAMSIZ]; + char addrbuf[NI_MAXHOST]; + struct ifaddrs *ifap, *ifa; + struct ifmaddrs *ifma; + sockunion_t lastifasa; + sockunion_t *psa, *pgsa, *pllsa, *pifasa; + char *pcolon; + char *pafname; + uint32_t lastifindex, thisifindex; + int error; + uint32_t ifindex = 0; + + if (interface != NULL) + ifindex = if_nametoindex(interface); + + error = 0; + ifap = NULL; + lastifindex = 0; + thisifindex = 0; + lastifasa.ss.ss_family = AF_UNSPEC; + + if (getifaddrs(&ifap) != 0) { + warn("getifmaddrs"); + return (-1); + } + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + error = 0; + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + + psa = (sockunion_t *)ifma->ifma_name; + if (psa->sa.sa_family != AF_LINK) { + fprintf(stderr, + "WARNING: Kernel returned invalid data.\n"); + error = -1; + break; + } + + /* Filter on interface name. */ + thisifindex = psa->sdl.sdl_index; + if (ifindex != 0 && thisifindex != ifindex) + continue; + + /* Filter on address family. */ + pgsa = (sockunion_t *)ifma->ifma_addr; + if (af != 0 && pgsa->sa.sa_family != af) + continue; + + strlcpy(thisifname, link_ntoa(&psa->sdl), sizeof(thisifname)); + pcolon = strchr(thisifname, ':'); + if (pcolon) + *pcolon = '\0'; + + /* Only print the banner for the first ifmaddrs entry. */ + if (lastifindex == 0 || lastifindex != thisifindex) { + lastifindex = thisifindex; + fprintf(stdout, "%s:\n", thisifname); + } + + /* + * Currently, multicast joins only take place on the + * primary IPv4 address, and only on the link-local IPv6 + * address, as per IGMPv2/3 and MLDv1/2 semantics. + * Therefore, we only look up the primary address on + * the first pass. + */ + pifasa = NULL; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if ((strcmp(ifa->ifa_name, thisifname) != 0) || + (ifa->ifa_addr == NULL) || + (ifa->ifa_addr->sa_family != pgsa->sa.sa_family)) + continue; + /* + * For AF_INET6 only the link-local address should + * be returned. If built without IPv6 support, + * skip this address entirely. + */ + pifasa = (sockunion_t *)ifa->ifa_addr; + if (pifasa->sa.sa_family == AF_INET6 +#ifdef INET6 + && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr) +#endif + ) { + pifasa = NULL; + continue; + } + break; + } + if (pifasa == NULL) + continue; /* primary address not found */ + + if (!vflag && pifasa->sa.sa_family == AF_LINK) + continue; + + /* Parse and print primary address, if not already printed. */ + if (lastifasa.ss.ss_family == AF_UNSPEC || + ((lastifasa.ss.ss_family == AF_LINK && + !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) || + !sa_equal(&lastifasa.sa, &pifasa->sa))) { + + switch (pifasa->sa.sa_family) { + case AF_INET: + pafname = "inet"; + break; + case AF_INET6: + pafname = "inet6"; + break; + case AF_LINK: + pafname = "link"; + break; + default: + pafname = "unknown"; + break; + } + + switch (pifasa->sa.sa_family) { + case AF_INET6: +#ifdef INET6 + { + const char *p = + inet6_n2a(&pifasa->sin6.sin6_addr); + strlcpy(addrbuf, p, sizeof(addrbuf)); + break; + } +#else + /* FALLTHROUGH */ +#endif + case AF_INET: + error = getnameinfo(&pifasa->sa, + pifasa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, + NI_NUMERICHOST); + if (error) + printf("getnameinfo: %s\n", + gai_strerror(error)); + break; + case AF_LINK: { + (void) sdl_addr_to_hex(&pifasa->sdl, addrbuf, + sizeof (addrbuf)); + break; + } + default: + addrbuf[0] = '\0'; + break; + } + + fprintf(stdout, "\t%s %s\n", pafname, addrbuf); + /* + * Print per-link IGMP information, if available. + */ + if (pifasa->sa.sa_family == AF_INET) { + struct igmp_ifinfo igi; + size_t mibsize, len; + int mib[5]; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet.igmp.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct igmp_ifinfo); + if (sysctl(mib, mibsize + 1, &igi, &len, NULL, + 0) == -1) { + perror("sysctl net.inet.igmp.ifinfo"); + goto next_ifnet; + } + in_ifinfo(&igi); + } +#ifdef INET6 + /* + * Print per-link MLD information, if available. + */ + if (pifasa->sa.sa_family == AF_INET6) { + struct mld_ifinfo mli; + size_t mibsize, len; + int mib[5]; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet6.mld.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct mld_ifinfo); + if (sysctl(mib, mibsize + 1, &mli, &len, NULL, + 0) == -1) { + perror("sysctl net.inet6.mld.ifinfo"); + goto next_ifnet; + } + in6_ifinfo(&mli); + } +#endif /* INET6 */ +#if defined(INET6) +next_ifnet: +#endif + lastifasa = *pifasa; + } + + /* Print this group address. */ +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6) { + const char *p = inet6_n2a(&pgsa->sin6.sin6_addr); + strlcpy(addrbuf, p, sizeof(addrbuf)); + } else +#endif + if (pgsa->sa.sa_family == AF_INET) { + error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); + if (error) + printf("getnameinfo: %s\n", + gai_strerror(error)); + } else { + (void) sdl_addr_to_hex(&pgsa->sdl, addrbuf, + sizeof (addrbuf)); + } + + fprintf(stdout, "\t\tgroup %s", addrbuf); + if (pgsa->sa.sa_family == AF_INET) { + inm_print_sources_sysctl(thisifindex, + pgsa->sin.sin_addr); + } +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6) { + in6m_print_sources_sysctl(thisifindex, + &pgsa->sin6.sin6_addr); + } +#endif + fprintf(stdout, "\n"); + + /* Link-layer mapping, if present. */ + pllsa = (sockunion_t *)ifma->ifma_lladdr; + if (pllsa != NULL) { + (void) sdl_addr_to_hex(&pllsa->sdl, addrbuf, + sizeof (addrbuf)); + fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf); + } + } + + if (ifap != NULL) + freeifaddrs(ifap); + + return (error); +} + +static void +in_ifinfo(struct igmp_ifinfo *igi) +{ + + printf("\t"); + switch (igi->igi_version) { + case IGMP_VERSION_1: + case IGMP_VERSION_2: + case IGMP_VERSION_3: + printf("igmpv%d", igi->igi_version); + break; + default: + printf("igmpv?(%d)", igi->igi_version); + break; + } + printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK"); + if (igi->igi_version == IGMP_VERSION_3) { + printf(" rv %u qi %u qri %u uri %u", + igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u v3timer %u", + igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer); + } + printf("\n"); +} + +static const char *inm_modes[] = { + "undefined", + "include", + "exclude", +}; + +static const char * +inm_mode(u_int mode) +{ + + if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) + return (inm_modes[mode]); + return (NULL); +} + +/* + * Retrieve per-group source filter mode and lists via sysctl. + */ +static void +inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina) +{ +#define MAX_SYSCTL_TRY 5 + int mib[7]; + int ntry = 0; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + mib[6] = gina.s_addr; /* 32 bits wide */ + mibsize = sizeof(mib) / sizeof(mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet.ip.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in_addr); + pina = (struct in_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), + inet_ntoa(*pina++)); + len -= sizeof(struct in_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet.ip.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +#ifdef INET6 + +static void +in6_ifinfo(struct mld_ifinfo *mli) +{ + + printf("\t"); + switch (mli->mli_version) { + case MLD_VERSION_1: + case MLD_VERSION_2: + printf("mldv%d", mli->mli_version); + break; + default: + printf("mldv?(%d)", mli->mli_version); + break; + } + printb(" flags", mli->mli_flags, "\020\1SILENT"); + if (mli->mli_version == MLD_VERSION_2) { + printf(" rv %u qi %u qri %u uri %u", + mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u", mli->mli_v1_timer, + mli->mli_v2_timer); + } + printf("\n"); +} + +/* + * Retrieve MLD per-group source filter mode and lists via sysctl. + * + * Note: The 128-bit IPv6 group addres needs to be segmented into + * 32-bit pieces for marshaling to sysctl. So the MIB name ends + * up looking like this: + * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3] + * Assumes that pgroup originated from the kernel, so its components + * are already in network-byte order. + */ +static void +in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup) +{ +#define MAX_SYSCTL_TRY 5 + char addrbuf[INET6_ADDRSTRLEN]; + int mib[10]; + int ntry = 0; + int *pi; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in6_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib, + &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + pi = (int *)pgroup; + for (i = 0; i < 4; i++) + mib[6 + i] = *pi++; + + mibsize = sizeof(mib) / sizeof(mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet6.ip6.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in6_addr); + pina = (struct in6_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + inet_ntop(AF_INET6, (const char *)pina++, addrbuf, + INET6_ADDRSTRLEN); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf); + len -= sizeof(struct in6_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet6.ip6.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +static const char * +inet6_n2a(struct in6_addr *p) +{ + static char buf[NI_MAXHOST]; + struct sockaddr_in6 sin6; + u_int32_t scopeid; + const int niflags = NI_NUMERICHOST; + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *p; + if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) || + IN6_IS_ADDR_MC_NODELOCAL(p)) { + scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); + if (scopeid) { + sin6.sin6_scope_id = scopeid; + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } + } + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + buf, sizeof(buf), NULL, 0, niflags) == 0) { + return (buf); + } else { + return ("(invalid)"); + } +} +#endif /* INET6 */ + +/* + * Print a value a la the %b format of the kernel's printf + */ +void +printb(const char *s, unsigned int v, const char *bits) +{ + int i, any = 0; + char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++) != '\0') { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +/* + * convert hardware address to hex string for logging errors. + */ +static const char * +sdl_addr_to_hex(const struct sockaddr_dl *sdl, char *orig_buf, int buflen) +{ + char *buf = orig_buf; + int i; + const u_char *lladdr; + int maxbytes = buflen / 3; + + lladdr = (u_char *)(size_t)sdl->sdl_data + sdl->sdl_nlen; + + if (maxbytes > sdl->sdl_alen) { + maxbytes = sdl->sdl_alen; + } + *buf = '\0'; + for (i = 0; i < maxbytes; i++) { + snprintf(buf, 3, "%02x", lladdr[i]); + buf += 2; + *buf = (i == maxbytes - 1) ? '\0' : ':'; + buf++; + } + return (orig_buf); +} + diff --git a/network_cmds/netstat.tproj/misc.c b/network_cmds/netstat.tproj/misc.c new file mode 100644 index 0000000..ac2a2ca --- /dev/null +++ b/network_cmds/netstat.tproj/misc.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include <sys/sysctl.h> + +#include <net/net_api_stats.h> +#include <err.h> +#include <stdio.h> + +#include "netstat.h" + +void +print_net_api_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct net_api_stats pnet_api_stats; + struct net_api_stats net_api_stats; + size_t len = sizeof(struct net_api_stats); + const char *mibvar = "net.api_stats"; + + if (sysctlbyname(mibvar, &net_api_stats, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + +#define STATDIFF(f) (net_api_stats.f - pnet_api_stats.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + + p(nas_iflt_attach_count, "\t%lld interface filter%s currently attached\n"); + p(nas_iflt_attach_total, "\t%lld interface filter%s attached since boot\n"); + p(nas_iflt_attach_os_total, "\t%lld interface filter%s attached since boot by OS\n"); + + p(nas_ipf_add_count, "\t%lld IP filter%s currently attached\n"); + p(nas_ipf_add_total, "\t%lld IP filter%s attached since boot\n"); + p(nas_ipf_add_os_total, "\t%lld IP filter%s attached since boot by OS\n"); + + p(nas_sfltr_register_count, "\t%lld socket filter%s currently attached\n"); + p(nas_sfltr_register_total, "\t%lld socket filter%s attached since boot\n"); + p(nas_sfltr_register_os_total, "\t%lld socket filter%s attached since boot by OS\n"); + + p(nas_socket_alloc_total, "\t%lld socket%s allocated since boot\n"); + p(nas_socket_in_kernel_total, "\t%lld socket%s allocated in-kernel since boot\n"); + p(nas_socket_in_kernel_os_total, "\t%lld socket%s allocated in-kernel by OS\n"); + p(nas_socket_necp_clientuuid_total, "\t%lld socket%s with NECP client UUID since boot\n"); + + p(nas_socket_domain_local_total, "\t%lld local domain socket%s allocated since boot\n"); + p(nas_socket_domain_route_total, "\t%lld route domain socket%s allocated since boot\n"); + p(nas_socket_domain_inet_total, "\t%lld inet domain socket%s allocated since boot\n"); + p(nas_socket_domain_inet6_total, "\t%lld inet6 domain socket%s allocated since boot\n"); + p(nas_socket_domain_system_total, "\t%lld system domain socket%s allocated since boot\n"); + p(nas_socket_domain_multipath_total, "\t%lld multipath domain socket%s allocated since boot\n"); + p(nas_socket_domain_key_total, "\t%lld key domain socket%s allocated since boot\n"); + p(nas_socket_domain_ndrv_total, "\t%lld ndrv domain socket%s allocated since boot\n"); + p(nas_socket_domain_other_total, "\t%lld other domains socket%s allocated since boot\n"); + + p(nas_socket_inet_stream_total, "\t%lld IPv4 stream socket%s created since boot\n"); + p(nas_socket_inet_dgram_total, "\t%lld IPv4 datagram socket%s created since boot\n"); + p(nas_socket_inet_dgram_connected, "\t%lld IPv4 datagram socket%s connected\n"); + p(nas_socket_inet_dgram_dns, "\t%lld IPv4 DNS socket%s\n"); + p(nas_socket_inet_dgram_no_data, "\t%lld IPv4 datagram socket%s without data\n"); + + p(nas_socket_inet6_stream_total, "\t%lld IPv6 stream socket%s created since boot\n"); + p(nas_socket_inet6_dgram_total, "\t%lld IPv6 datagram socket%s created since boot\n"); + p(nas_socket_inet6_dgram_connected, "\t%lld IPv6 datagram socket%s connected\n"); + p(nas_socket_inet6_dgram_dns, "\t%lld IPv6 DNS socket%s\n"); + p(nas_socket_inet6_dgram_no_data, "\t%lld IPv6 datagram socket%s without data\n"); + + p(nas_socket_mcast_join_total, "\t%lld socket multicast join%s since boot\n"); + p(nas_socket_mcast_join_os_total, "\t%lld socket multicast join%s since boot by OS\n"); + + p(nas_nx_flow_inet_stream_total, "\t%lld IPv4 stream nexus flow%s added since boot\n"); + p(nas_nx_flow_inet_dgram_total, "\t%lld IPv4 datagram nexus flow%s added since boot\n"); + + p(nas_nx_flow_inet6_stream_total, "\t%lld IPv6 stream nexus flow%s added since boot\n"); + p(nas_nx_flow_inet6_dgram_total, "\t%lld IPv6 datagram nexus flow%s added since boot\n"); + + p(nas_ifnet_alloc_count, "\t%lld interface%s currently allocated\n"); + p(nas_ifnet_alloc_total, "\t%lld interface%s allocated since boot\n"); + p(nas_ifnet_alloc_os_count, "\t%lld interface%s currently allocated by OS\n"); + p(nas_ifnet_alloc_os_total, "\t%lld extended interface%s allocated since boot by OS\n"); + + p(nas_pf_addrule_total, "\t%lld PF addrule operation%s since boot\n"); + p(nas_pf_addrule_os, "\t%lld PF addrule operation%s since boot by OS\n"); + + p(nas_vmnet_total, "\t%lld vmnet start%s since boot\n"); + +#undef STATDIFF +#undef p +#undef p1a +} + diff --git a/network_cmds/netstat.tproj/mptcp.c b/network_cmds/netstat.tproj/mptcp.c new file mode 100644 index 0000000..70be928 --- /dev/null +++ b/network_cmds/netstat.tproj/mptcp.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#include <stdio.h> +#include <err.h> +#include <stdlib.h> +#include <strings.h> +#include <inttypes.h> + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netinet/mptcp_var.h> + +#include <arpa/inet.h> + +#include "netstat.h" + +/* XXX we can't include tcp_fsm.h because inet.c already includes it. */ +static const char *tcpstates[] = { + "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", + "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING", + "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT" +}; + +static const char *mptcpstates[] = { + "CLOSED", "LISTEN", "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", + "CLOSING", "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT", "TERMINATE" +}; + +int mptcp_done = 0; +extern void inetprint (struct in_addr *, int, char *, int); +extern void inet6print (struct in6_addr *, int, char *, int); + +static void +printmptcp(int id, conninfo_mptcp_t *mptcp) +{ + int i; + conninfo_tcp_t *tcpci; + struct sockaddr_storage *src, *dst; + mptcp_flow_t *flow; + int af; + + printf("mptcp/%-2.2d %-8.8x/%-8.8x %50s \n" + " [tok(%#"PRIx32") snd(%#"PRIx64") rcv(%#"PRIx64") " + "aid(%d)]\n", id, + mptcp->mptcpci_mpte_flags, mptcp->mptcpci_flags, + mptcpstates[mptcp->mptcpci_state], mptcp->mptcpci_rtoken, + mptcp->mptcpci_sndnxt, mptcp->mptcpci_rcvnxt, + mptcp->mptcpci_mpte_addrid); + + flow = (mptcp_flow_t*)((caddr_t)mptcp + mptcp->mptcpci_flow_offset); + + for (i = 0; i < mptcp->mptcpci_nflows; i++) { + src = &flow->flow_src; + dst = &flow->flow_dst; + af = src->ss_family; + printf(" tcp%d/%-2.2d ", af == AF_INET ? 4 : 6, + flow->flow_cid); + printf("%-8.8x ", flow->flow_flags); +#define SIN(x) ((struct sockaddr_in *)(x)) +#define SIN6(x) ((struct sockaddr_in6 *)(x)) + switch (af) { + case AF_INET: + inetprint(&SIN(src)->sin_addr, SIN(src)->sin_port, + "tcp", nflag); + inetprint(&SIN(dst)->sin_addr, SIN(dst)->sin_port, + "tcp", nflag); + break; +#ifdef INET6 + case AF_INET6: + inet6print(&SIN6(src)->sin6_addr, SIN6(src)->sin6_port, + "tcp", nflag); + inet6print(&SIN6(dst)->sin6_addr, SIN6(dst)->sin6_port, + "tcp", nflag); + break; + } +#endif +#undef SIN +#undef SIN6 + tcpci = (conninfo_tcp_t*)((caddr_t)flow + + flow->flow_tcpci_offset); + printf("%s \n" + " [relseq(%-4.4d), err(%d)]\n", + tcpstates[tcpci->tcpci_tcp_info.tcpi_state], + flow->flow_relseq, + flow->flow_soerror); + + flow = (mptcp_flow_t*)((caddr_t)flow + flow->flow_len); + } +} + +void +mptcppr(uint32_t off, char *name, int af) +{ +#pragma unused(off, name, af) + const char *mibvar = "net.inet.mptcp.pcblist"; + size_t len = 0; + conninfo_mptcp_t *mptcp; + char *buf, *bufp; + int id = 0; + + if (Lflag || Aflag || mptcp_done) + return; + mptcp_done = 1; + + if (sysctlbyname(mibvar, 0, &len, NULL, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == NULL) { + warn("malloc"); + return; + } + if (sysctlbyname(mibvar, buf, &len, NULL, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + printf("Active Multipath Internet connections\n"); + printf("%-8.8s %-9.9s %-22.22s %-22.22s %-11.11s\n", + "Proto/ID", "Flags", + "Local Address", "Foreign Address", + "(state)"); + + bufp = buf; + while (bufp < buf + len) { + /* Sanity check */ + if (buf + len - bufp < sizeof(conninfo_mptcp_t)) + break; + mptcp = (conninfo_mptcp_t *)bufp; + printmptcp(id++, mptcp); + bufp += mptcp->mptcpci_len; + } + free(buf); +} diff --git a/network_cmds/netstat.tproj/netstat.1 b/network_cmds/netstat.tproj/netstat.1 new file mode 100644 index 0000000..61ab843 --- /dev/null +++ b/network_cmds/netstat.tproj/netstat.1 @@ -0,0 +1,445 @@ +.\" Copyright (c) 2015 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (c) 1983, 1990, 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)netstat.1 8.8 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/netstat/netstat.1,v 1.22.2.7 2001/08/10 09:07:09 ru Exp $ +.\" +.Dd June 15, 2001 +.Dt NETSTAT 1 +.Os Darwin +.Sh NAME +.Nm netstat +.Nd show network status +.Sh SYNOPSIS +.Nm +.Op Fl AaLlnW +.Op Fl f Ar address_family | Fl p Ar protocol +.Nm +.Op Fl gilns +.Op Fl v +.Op Fl f Ar address_family +.Op Fl I Ar interface +.Nm +.Fl i | I Ar interface +.Op Fl w Ar wait +.Op Fl c Ar queue +.Op Fl abdgqRtS +.Nm +.Fl s Op Fl s +.Op Fl f Ar address_family | Fl p Ar protocol +.Op Fl w Ar wait +.Nm +.Fl i | I Ar interface Fl s +.Op Fl f Ar address_family | Fl p Ar protocol +.Nm +.Fl m +.Op Fl m +.Nm +.Fl r +.Op Fl Aaln +.Op Fl f Ar address_family +.Nm +.Fl rs +.Op Fl s +.\"----------------------------------------------------------------------------------------- +.Sh DESCRIPTION +.\"----------------------------------------------------------------------------------------- +The +.Nm +command symbolically displays the contents of various network-related data structures. +There are a number of output formats, depending on the options for the information presented. +The first form of the command displays a list of active sockets for each protocol. +The second form presents the contents of one of the other network data structures according +to the option selected. Using the third form, with a +.Ar wait +interval specified, +.Nm +will continuously display the information regarding packet traffic on the configured network +interfaces. The fourth form displays statistics for the specified protocol or address family. If a +.Ar wait +interval is specified, the protocol information over the last interval seconds will be displayed. +The fifth form displays per-interface statistics for the specified protocol or address family. +The sixth form displays +.Xr mbuf 9 +statistics. The seventh form displays routing table for the specified address family. The +eighth form displays routing statistics. +.Pp +The options have the following meaning: +.Bl -tag -width flag +.It Fl A +With the default display, show the address of any protocol control blocks associated with +sockets and the flow hash; used for debugging. +.It Fl a +With the default display, show the state of all sockets; normally sockets used by server +processes are not shown. With the routing table display (option +.Fl r , +as described below), show protocol-cloned routes (routes generated by a +.Dv RTF_PRCLONING +parent route); normally these routes are not shown. +.It Fl b +With the interface display (option +.Fl i , +as described below), show the number of bytes in and out. +.It Fl c Ar queue +With the queue statistics (option +.Fl q , +as described below), show only those for the specified +.Ar queue . +.It Fl d +With either interface display (option +.Fl i +or an interval, as described below), show the number of dropped packets. +.It Fl f Ar address_family +Limit statistics or address control block reports to those of the specified +.Ar address family . +The following address families are recognized: +.Ar inet , +for +.Dv AF_INET , +.Ar inet6 , +for +.Dv AF_INET6 +and +.Ar unix , +for +.Dv AF_UNIX . +.It Fl g +Show information related to multicast (group address) membership. If the +.Fl s +option is also present, show extended interface group management statistics. If the +.Fl v +option is specified, show link-layer memberships; they are suppressed by default. +Source lists for each group will also be printed. Specifiying +.Fl v +twice will print the control plane timers for each interface and the source list counters +for each group. If the +.Fl i +is specified, only that interface will be shown. If the +.Fl f +is specified, only information for the address family will be displayed. +.It Fl I Ar interface +Show information about the specified interface; used with a +.Ar wait +interval as described below. +If the +.Fl s +option is present, show per-interface protocol statistics on the +.Ar interface +for the specified +.Ar address_family +or +.Ar protocol , +or for all protocol families. +.It Fl i +Show the state of interfaces which have been auto-configured (interfaces statically +configured into a system, but not located at boot time are not shown). If the +.Fl a +options is also present, multicast addresses currently in use are shown for each +Ethernet interface and for each IP interface address. Multicast addresses are shown +on separate lines following the interface address with which they are associated. +If the +.Fl s +option is present, show per-interface statistics on all interfaces for the specified +.Ar address_family +or +.Ar protocol , +or for all protocol families. +.It Fl L +Show the size of the various listen queues. The first count shows the number of +unaccepted connections. The second count shows the amount of unaccepted incomplete +connections. The third count is the maximum number of queued connections. +.It Fl l +Print full IPv6 address. +.It Fl m +Show statistics recorded by the memory management routines (the network stack manages a private pool of memory buffers). More detailed information about the buffers, which includes their cache related statistics, can be obtained by using +.Fl mm +or +.Fl m +.Fl m +option. +.It Fl n +Show network addresses as numbers (normally +.Nm +interprets addresses and attempts to display them symbolically). This option may be +used with any of the display formats. +.It Fl p Ar protocol +Show statistics about +.Ar protocol , +which is either a well-known name for a protocol or an alias for it. Some protocol +names and aliases are listed in the file +.Pa /etc/protocols . +The special protocol name +.Dq bdg +is used to show bridging statistics. A null response typically means that there are +no interesting numbers to report. The program will complain if +.Ar protocol +is unknown or if there is no statistics routine for it. +.It Fl q +Show network interface send queue statistics. By default all queues are displayed, unless +specified with +.Fl c . +This option requires specifying an interface with +.Fl I +option. More detailed information about the queues, which includes their queueing algorithm related statistics, can be obtained by using +.Fl qq +or +.Fl q +.Fl q +option. +.It Fl r +Show the routing tables. Use with +.Fl a +to show protocol-cloned routes. When +.Fl s +is also present, show routing statistics instead. When +.Fl l +is also present, +.Nm +assumes more columns are there and the maximum transmission unit. +More detailed information about the route metrics are displayed with +.Fl ll +for TCP round trip times +.Fl lll +for all metrics. +Use the +.Fl z +flags to display only entries with non-zero RTT values. +.Pq Dq mtu +are also displayed. +.It Fl R +Show reachability information. Use with +.Fl i +to show link-layer reachability information for a given interface. +.It Fl s +Show per-protocol statistics. If this option is repeated, counters with a value of +zero are suppressed. For security reasons, root privileges are required to read TCP statistics and in the absence of such privileges all TCP counters will be reported as zero. +.It Fl S +Show interface link status and interface state information about the specified interface. This option requires specifying an interface with +.Fl I +option. +.It Fl v +Increase verbosity level. +.It Fl W +In certain displays, avoid truncating addresses even if this causes some fields to +overflow. +.It Fl w Ar wait +Show network interface or protocol statistics at intervals of +.Ar wait +seconds. +.It Fl x +Show extended link-layer reachability information in addition to that shown by +the +.Fl R +flag. +.El +.Pp +.\"------------------------------------------------------------------------------- +.Sh OUTPUT +.\"------------------------------------------------------------------------------- +The default display, for active sockets, shows the local and remote addresses, +send and receive queue sizes (in bytes), protocol, and the internal state of +the protocol. Address formats are of the form +.Dq host.port +or +.Dq network.port +if a socket's address specifies a network but no specific host address. +If known, the host and network addresses are displayed symbolically +according to the databases +.Pa /etc/hosts +and +.Pa /etc/networks , +respectively. If a symbolic name for an address is unknown, or if the +.Fl n +option is specified, the address is printed numerically, according to the +address family. For more information regarding the Internet +.Dq dot format , +refer to +.Xr inet 3 ) . +Unspecified, +or +.Dq wildcard , +addresses and ports appear as +.Dq * . +.Pp +Internet domain socket states: +.Bl -column X LISTEN +CLOSED: The socket is not in use. +.Pp +LISTEN: The socket is listening for incoming connections. Unconnected +listening sockets like these are only displayed when using the -a option. +.Pp +SYN_SENT: The socket is actively trying to establish a connection to a +remote peer. +.Pp +SYN_RCVD: The socket has passively received a connection request from a +remote peer. +.Pp +ESTABLISHED: The socket has an established connection between a local +application and a remote peer. +.Pp +CLOSE_WAIT: The socket connection has been closed by the remote peer, +and the system is waiting for the local application to close its half of +the connection. +.Pp +LAST_ACK: The socket connection has been closed by the remote peer, the +local application has closed its half of the connection, and the system +is waiting for the remote peer to acknowledge the close. +.Pp +FIN_WAIT_1: The socket connection has been closed by the local +application, the remote peer has not yet acknowledged the close, and the +system is waiting for it to close its half of the connection. +.Pp +FIN_WAIT_2: The socket connection has been closed by the local +application, the remote peer has acknowledged the close, and the system +is waiting for it to close its half of the connection. +.Pp +CLOSING: The socket connection has been closed by the local application +and the remote peer simultaneously, and the remote peer has not yet +acknowledged the close attempt of the local application. +.Pp +TIME_WAIT: The socket connection has been closed by the local +application, the remote peer has closed its half of the connection, and +the system is waiting to be sure that the remote peer received the last +acknowledgement. +.El +.Pp +The interface display provides a table of cumulative statistics regarding +packets transferred, errors, and collisions. The network addresses of the +interface and the maximum transmission unit +.Pq Dq mtu +are also displayed. +.Pp +The routing table display indicates the available routes and their status. +Each route consists of a destination host or network and a gateway to use +in forwarding packets. The flags field shows a collection of information +about the route stored as binary choices. The individual flags are discussed +in more detail in the +.Xr route 8 +and +.Xr route 4 +manual pages. The mapping between letters and flags is: +.Bl -column XXXX RTF_BLACKHOLE +1 RTF_PROTO1 Protocol specific routing flag #1 +2 RTF_PROTO2 Protocol specific routing flag #2 +3 RTF_PROTO3 Protocol specific routing flag #3 +B RTF_BLACKHOLE Just discard packets (during updates) +b RTF_BROADCAST The route represents a broadcast address +C RTF_CLONING Generate new routes on use +c RTF_PRCLONING Protocol-specified generate new routes on use +D RTF_DYNAMIC Created dynamically (by redirect) +G RTF_GATEWAY Destination requires forwarding by intermediary +H RTF_HOST Host entry (net otherwise) +I RTF_IFSCOPE Route is associated with an interface scope +i RTF_IFREF Route is holding a reference to the interface +L RTF_LLINFO Valid protocol to link address translation +M RTF_MODIFIED Modified dynamically (by redirect) +m RTF_MULTICAST The route represents a multicast address +R RTF_REJECT Host or net unreachable +r RTF_ROUTER Host is a default router +S RTF_STATIC Manually added +U RTF_UP Route usable +W RTF_WASCLONED Route was generated as a result of cloning +X RTF_XRESOLVE External daemon translates proto to link address +Y RTF_PROXY Proxying; cloned routes will not be scoped +.El +.Pp +Direct routes are created for each interface attached to the local host; +the gateway field for such entries shows the address of the outgoing +interface. The refcnt field gives the current number of active uses of +the route. Connection oriented protocols normally hold on to a single +route for the duration of a connection while connectionless protocols +obtain a route while sending to the same destination. The use field +provides a count of the number of packets sent using that route. The +interface entry indicates the network interface utilized for the route. +A route which is marked with the RTF_IFSCOPE flag is instantiated for +the corresponding interface. A cloning route which is marked with the +RTF_PROXY flag will not generate new routes that are associated +with its interface scope. +.Pp +When +.Nm netstat +is invoked with the +.Fl w +option and a +.Ar wait +interval argument, it displays a running count of statistics related to +network interfaces or protocols. An obsolete version of this option used a numeric +parameter with no option, and is currently supported for backward +compatibility. By default, this display summarizes information for all +interfaces. Information for a specific interface may be displayed with the +.Fl I +option. +.Sh SEE ALSO +.Xr nfsstat 1 , +.Xr ps 1 , +.Xr inet 4 , +.Xr unix 4 , +.Xr hosts 5 , +.Xr networks 5 , +.Xr protocols 5 , +.Xr route 8 , +.Xr services 5 , +.Xr iostat 8 , +.Sh HISTORY +The +.Nm netstat +command appeared in +.Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. +.Sh BUGS +The notion of errors is ill-defined. diff --git a/network_cmds/netstat.tproj/netstat.h b/network_cmds/netstat.tproj/netstat.h new file mode 100644 index 0000000..68511da --- /dev/null +++ b/network_cmds/netstat.tproj/netstat.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2008-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1992, 1993 + * Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)netstat.h 8.2 (Berkeley) 1/4/94 + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <stdint.h> + +#include <TargetConditionals.h> + +extern int Aflag; /* show addresses of protocol control block */ +extern int aflag; /* show all sockets (including servers) */ +extern int bflag; /* show i/f total bytes in/out */ +extern int cflag; /* show specific classq */ +extern int dflag; /* show i/f dropped packets */ +extern int Fflag; /* show i/f forwarded packets */ +extern int gflag; /* show group (multicast) routing or stats */ +extern int iflag; /* show interfaces */ +extern int lflag; /* show routing table with use and ref */ +extern int Lflag; /* show size of listen queues */ +extern int mflag; /* show memory stats */ +extern int nflag; /* show addresses numerically */ +extern int Rflag; /* show reachability information */ +extern int rflag; /* show routing tables (or routing stats) */ +extern int sflag; /* show protocol statistics */ +extern int prioflag; /* show packet priority statistics */ +extern int tflag; /* show i/f watchdog timers */ +extern int vflag; /* more verbose */ +extern int Wflag; /* wide display */ +extern int qflag; /* Display ifclassq stats */ +extern int Qflag; /* Display opportunistic polling stats */ +extern int xflag; /* show extended link-layer reachability information */ +extern int zflag; /* show only entries with non zero rtt metrics */ + +extern int cq; /* send classq index (-1 for all) */ +extern int interval; /* repeat interval for i/f stats */ + +extern char *interface; /* desired i/f for stats, or NULL for all i/fs */ +extern int unit; /* unit number for above */ + +extern int af; /* address family */ + +extern char *plural(int); +extern char *plurales(int); +extern char *pluralies(int); + +extern void protopr(uint32_t, char *, int); +extern void mptcppr(uint32_t, char *, int); +extern void tcp_stats(uint32_t, char *, int); +extern void mptcp_stats(uint32_t, char *, int); +extern void udp_stats(uint32_t, char *, int); +extern void ip_stats(uint32_t, char *, int); +extern void icmp_stats(uint32_t, char *, int); +extern void igmp_stats(uint32_t, char *, int); +extern void arp_stats(uint32_t, char *, int); +#ifdef IPSEC +extern void ipsec_stats(uint32_t, char *, int); +#endif + +#ifdef INET6 +extern void ip6_stats(uint32_t, char *, int); +extern void ip6_ifstats(char *); +extern void icmp6_stats(uint32_t, char *, int); +extern void icmp6_ifstats(char *); +extern void rip6_stats(uint32_t, char *, int); + +/* forward references */ +struct sockaddr_in6; +struct in6_addr; +struct sockaddr; + +extern char *routename6(struct sockaddr_in6 *); +extern char *netname6(struct sockaddr_in6 *, struct sockaddr *); +#endif /*INET6*/ + +#ifdef IPSEC +extern void pfkey_stats(uint32_t, char *, int); +#endif + +extern void systmpr(uint32_t, char *, int); +extern void kctl_stats(uint32_t, char *, int); +extern void kevt_stats(uint32_t, char *, int); + +extern void mbpr(void); + +extern void intpr(void (*)(char *)); +extern void intpr_ri(void (*)(char *)); +extern void intervalpr(void (*)(uint32_t, char *, int), uint32_t, + char *, int); + +extern void pr_rthdr(int); +extern void pr_family(int); +extern void rt_stats(void); +extern void upHex(char *); +extern char *routename(uint32_t); +extern char *netname(uint32_t, uint32_t); +extern void routepr(void); + +extern void unixpr(void); +extern void aqstatpr(void); +extern void rxpollstatpr(void); +extern void vsockpr(uint32_t,char *,int); + +extern void ifmalist_dump(void); + +extern int print_time(void); +extern void print_link_status(const char *); + +extern void print_extbkidle_stats(uint32_t, char *, int); +extern void print_nstat_stats(uint32_t, char *, int); +extern void print_net_api_stats(uint32_t, char *, int); + diff --git a/network_cmds/netstat.tproj/route.c b/network_cmds/netstat.tproj/route.c new file mode 100644 index 0000000..943e3c8 --- /dev/null +++ b/network_cmds/netstat.tproj/route.c @@ -0,0 +1,795 @@ +/* + * Copyright (c) 2008-2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdint.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> +#include <net/radix.h> + +#include <netinet/in.h> + +#include <sys/sysctl.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <err.h> +#include <time.h> +#include "netstat.h" + +/* alignment constraint for routing socket */ +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +/* + * Definitions for showing gateway flags. + */ +struct bits { + uint32_t b_mask; + char b_val; +} bits[] = { + { RTF_UP, 'U' }, + { RTF_GATEWAY, 'G' }, + { RTF_HOST, 'H' }, + { RTF_REJECT, 'R' }, + { RTF_DYNAMIC, 'D' }, + { RTF_MODIFIED, 'M' }, + { RTF_MULTICAST,'m' }, + { RTF_DONE, 'd' }, /* Completed -- for routing messages only */ + { RTF_CLONING, 'C' }, + { RTF_XRESOLVE, 'X' }, + { RTF_LLINFO, 'L' }, + { RTF_STATIC, 'S' }, + { RTF_PROTO1, '1' }, + { RTF_PROTO2, '2' }, + { RTF_WASCLONED,'W' }, + { RTF_PRCLONING,'c' }, + { RTF_PROTO3, '3' }, + { RTF_BLACKHOLE,'B' }, + { RTF_BROADCAST,'b' }, + { RTF_IFSCOPE, 'I' }, + { RTF_IFREF, 'i' }, + { RTF_PROXY, 'Y' }, + { RTF_ROUTER, 'r' }, + { 0 } +}; + +typedef union { + uint32_t dummy; /* Helps align structure. */ + struct sockaddr u_sa; + u_short u_data[128]; +} sa_u; + +static void np_rtentry __P((struct rt_msghdr2 *)); +static void p_sockaddr __P((struct sockaddr *, struct sockaddr *, int, int)); +static void p_flags __P((int, char *)); +static uint32_t forgemask __P((uint32_t)); +static void domask __P((char *, uint32_t, uint32_t)); + +/* + * Print address family header before a section of the routing table. + */ +void +pr_family(int af) +{ + char *afname; + + switch (af) { + case AF_INET: + afname = "Internet"; + break; +#ifdef INET6 + case AF_INET6: + afname = "Internet6"; + break; +#endif /*INET6*/ + case AF_IPX: + afname = "IPX"; + break; + default: + afname = NULL; + break; + } + if (afname) + printf("\n%s:\n", afname); + else + printf("\nProtocol Family %d:\n", af); +} + +/* column widths; each followed by one space */ +#ifndef INET6 +#define WID_DST(af) 18 /* width of destination column */ +#define WID_GW(af) 18 /* width of gateway column */ +#define WID_RT_IFA(af) 18 /* width of source column */ +#define WID_IF(af) 7 /* width of netif column */ +#else +#define WID_DST(af) \ + ((af) == AF_INET6 ? (lflag ? 39 : (nflag ? 39: 18)) : 18) +#define WID_GW(af) \ + ((af) == AF_INET6 ? (lflag ? 31 : (nflag ? 31 : 18)) : 18) +#define WID_RT_IFA(af) \ + ((af) == AF_INET6 ? (lflag ? 39 : (nflag ? 39 : 18)) : 18) +#define WID_IF(af) ((af) == AF_INET6 ? 8 : 7) +#endif /*INET6*/ + +/* + * Print header for routing table columns. + */ +void +pr_rthdr(int af) +{ + if (lflag) { + if (lflag > 2) + printf("%-*.*s %-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s " + "%10s %10s %8s %8s %8s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + WID_RT_IFA(af), WID_RT_IFA(af), "RT_IFA", + "Flags", "Refs", "Use", "Mtu", + WID_IF(af), WID_IF(af), "Netif", "Expire", + "rtt(ms)", "rttvar(ms)", "recvpipe", "sendpipe", "ssthresh"); + else if (lflag > 1) + printf("%-*.*s %-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s " + "%10s %10s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + WID_RT_IFA(af), WID_RT_IFA(af), "RT_IFA", + "Flags", "Refs", "Use", "Mtu", + WID_IF(af), WID_IF(af), "Netif", "Expire", + "rtt(ms)", "rttvar(ms)"); + else + printf("%-*.*s %-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + WID_RT_IFA(af), WID_RT_IFA(af), "RT_IFA", + "Flags", "Refs", "Use", "Mtu", + WID_IF(af), WID_IF(af), "Netif", "Expire"); + } else { + printf("%-*.*s %-*.*s %-10.10s %*.*s %6s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + "Flags", WID_IF(af), WID_IF(af), "Netif", "Expire"); + } +} + +/* + * Print routing tables. + */ +void +routepr(void) +{ + size_t extra_space; + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr2 *rtm; + int try = 1; + + printf("Routing tables\n"); + + again: + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_DUMP2; + mib[5] = 0; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { + err(1, "sysctl: net.route.0.0.dump estimate"); + } + /* allocate extra space in case the table grows */ + extra_space = needed / 2; + if (needed <= (SIZE_MAX - extra_space)) { + needed += extra_space; + } + if ((buf = malloc(needed)) == 0) { + err(2, "malloc(%lu)", (unsigned long)needed); + } + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { +#define MAX_TRIES 10 + if (errno == ENOMEM && try < MAX_TRIES) { + /* the buffer we provided was too small, try again */ + free(buf); + try++; + goto again; + } + err(1, "sysctl: net.route.0.0.dump"); + } + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr2 *)next; + np_rtentry(rtm); + } +} + +static void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa); + } else { + rti_info[i] = NULL; + } +} +} + +static void +np_rtentry(struct rt_msghdr2 *rtm) +{ + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + struct sockaddr *rti_info[RTAX_MAX]; + static int old_fam; + int fam = 0; + u_short lastindex = 0xffff; + static char ifname[IFNAMSIZ + 1]; + sa_u addr, mask; + + /* + * Don't print protocol-cloned routes unless -a. + */ + if ((rtm->rtm_flags & RTF_WASCLONED) && + (rtm->rtm_parentflags & RTF_PRCLONING) && + !aflag) { + return; + } + + if (lflag > 1 && zflag != 0 && rtm->rtm_rmx.rmx_rtt == 0 && rtm->rtm_rmx.rmx_rttvar == 0) + return; + fam = sa->sa_family; + if (af != AF_UNSPEC && af != fam) + return; + if (fam != old_fam) { + pr_family(fam); + pr_rthdr(fam); + old_fam = fam; + } + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + bzero(&addr, sizeof(addr)); + if ((rtm->rtm_addrs & RTA_DST)) + bcopy(rti_info[RTAX_DST], &addr, rti_info[RTAX_DST]->sa_len); + bzero(&mask, sizeof(mask)); + if ((rtm->rtm_addrs & RTA_NETMASK)) + bcopy(rti_info[RTAX_NETMASK], &mask, rti_info[RTAX_NETMASK]->sa_len); + p_sockaddr(&addr.u_sa, &mask.u_sa, rtm->rtm_flags, + WID_DST(addr.u_sa.sa_family)); + + p_sockaddr(rti_info[RTAX_GATEWAY], NULL, RTF_HOST, + WID_GW(addr.u_sa.sa_family)); + + if (lflag && (rtm->rtm_addrs & RTA_IFA)) { + p_sockaddr(rti_info[RTAX_IFA], NULL, RTF_HOST, + WID_RT_IFA(addr.u_sa.sa_family)); + } + + p_flags(rtm->rtm_flags, "%-10.10s "); + + if (lflag) { + printf("%6u %8u ", rtm->rtm_refcnt, (unsigned int)rtm->rtm_use); + if (rtm->rtm_rmx.rmx_mtu != 0) + printf("%6u ", rtm->rtm_rmx.rmx_mtu); + else + printf("%6s ", ""); + } + + if (rtm->rtm_index != lastindex) { + if_indextoname(rtm->rtm_index, ifname); + lastindex = rtm->rtm_index; + } + printf("%*.*s", WID_IF(addr.u_sa.sa_family), + WID_IF(addr.u_sa.sa_family), ifname); + + if (rtm->rtm_rmx.rmx_expire) { + time_t expire_time; + + if ((expire_time = + rtm->rtm_rmx.rmx_expire - time((time_t *)0)) > 0) + printf(" %6d", (int)expire_time); + else + printf(" %6s", "!"); + } else { + printf(" %6s", ""); + } + if (lflag > 1) { + if (rtm->rtm_rmx.rmx_rtt != 0) + printf(" %6u.%03u", rtm->rtm_rmx.rmx_rtt / 1000, + rtm->rtm_rmx.rmx_rtt % 1000); + else + printf(" %10s", ""); + if (rtm->rtm_rmx.rmx_rttvar != 0) + printf(" %6u.%03u", rtm->rtm_rmx.rmx_rttvar / 1000, + rtm->rtm_rmx.rmx_rttvar % 1000); + else + printf(" %10s", ""); + if (lflag > 2) { + if (rtm->rtm_rmx.rmx_recvpipe != 0) + printf(" %8u", rtm->rtm_rmx.rmx_recvpipe); + else + printf(" %8s", ""); + if (rtm->rtm_rmx.rmx_sendpipe != 0) + printf(" %8u", rtm->rtm_rmx.rmx_sendpipe); + else + printf(" %8s", ""); + if (rtm->rtm_rmx.rmx_ssthresh != 0) + printf(" %8u", rtm->rtm_rmx.rmx_ssthresh); + else + printf(" %8s", ""); + } + } + putchar('\n'); +} + +static void +p_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags, int width) +{ + char workbuf[128], *cplim; + char *cp = workbuf; + + switch(sa->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + if ((sin->sin_addr.s_addr == INADDR_ANY) && + mask && + (ntohl(((struct sockaddr_in *)mask)->sin_addr.s_addr) == 0L || mask->sa_len == 0)) + cp = "default" ; + else if (flags & RTF_HOST) + cp = routename(sin->sin_addr.s_addr); + else if (mask) + cp = netname(sin->sin_addr.s_addr, + ntohl(((struct sockaddr_in *)mask)-> + sin_addr.s_addr)); + else + cp = netname(sin->sin_addr.s_addr, 0L); + break; + } + +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + struct in6_addr *in6 = &sa6->sin6_addr; + + /* + * XXX: This is a special workaround for KAME kernels. + * sin6_scope_id field of SA should be set in the future. + */ + if (IN6_IS_ADDR_LINKLOCAL(in6) || + IN6_IS_ADDR_MC_NODELOCAL(in6) || + IN6_IS_ADDR_MC_LINKLOCAL(in6)) { + /* XXX: override is ok? */ + sa6->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)&in6->s6_addr[2]); + *(u_short *)&in6->s6_addr[2] = 0; + } + + if (flags & RTF_HOST) + cp = routename6(sa6); + else if (mask) + cp = netname6(sa6, mask); + else + cp = netname6(sa6, NULL); + break; + } +#endif /*INET6*/ + + case AF_LINK: { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + + if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && + sdl->sdl_slen == 0) { + (void) snprintf(workbuf, sizeof(workbuf), "link#%d", sdl->sdl_index); + } else { + switch (sdl->sdl_type) { + + case IFT_ETHER: { + int i; + u_char *lla = (u_char *)sdl->sdl_data + + sdl->sdl_nlen; + + cplim = ""; + for (i = 0; i < sdl->sdl_alen; i++, lla++) { + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), "%s%x", cplim, *lla); + cplim = ":"; + } + cp = workbuf; + break; + } + + default: + cp = link_ntoa(sdl); + break; + } + } + break; + } + + default: { + u_char *s = (u_char *)sa->sa_data, *slim; + + slim = sa->sa_len + (u_char *) sa; + cplim = cp + sizeof(workbuf) - 6; + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), "(%d)", sa->sa_family); + while (s < slim && cp < cplim) { + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), " %02x", *s++); + if (s < slim) + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), "%02x", *s++); + } + cp = workbuf; + } + } + if (width < 0 ) { + printf("%s ", cp); + } else { + if (nflag) + printf("%-*s ", width, cp); + else + printf("%-*.*s ", width, width, cp); + } +} + +static void +p_flags(int f, char *format) +{ + char name[33], *flags; + struct bits *p = bits; + + for (flags = name; p->b_mask; p++) + if (p->b_mask & f) + *flags++ = p->b_val; + *flags = '\0'; + printf(format, name); +} + +char * +routename(uint32_t in) +{ + char *cp; + static char line[MAXHOSTNAMELEN]; + struct hostent *hp; + + cp = 0; + if (!nflag) { + hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), + AF_INET); + if (hp) { + cp = hp->h_name; + //### trimdomain(cp, strlen(cp)); + } + } + if (cp) { + strlcpy(line, cp, sizeof(line)); + } else { +#define C(x) ((x) & 0xff) + in = ntohl(in); + snprintf(line, sizeof(line), "%u.%u.%u.%u", + C(in >> 24), C(in >> 16), C(in >> 8), C(in)); + } + return (line); +} + +static uint32_t +forgemask(uint32_t a) +{ + uint32_t m; + + if (IN_CLASSA(a)) + m = IN_CLASSA_NET; + else if (IN_CLASSB(a)) + m = IN_CLASSB_NET; + else + m = IN_CLASSC_NET; + return (m); +} + +static void +domask(char *dst, uint32_t addr, uint32_t mask) +{ + int b, i; + + if (!mask || (forgemask(addr) == mask)) { + *dst = '\0'; + return; + } + i = 0; + for (b = 0; b < 32; b++) + if (mask & (1 << b)) { + int bb; + + i = b; + for (bb = b+1; bb < 32; bb++) + if (!(mask & (1 << bb))) { + i = -1; /* noncontig */ + break; + } + break; + } + if (i == -1) + snprintf(dst, sizeof(dst), "&0x%x", mask); + else + snprintf(dst, sizeof(dst), "/%d", 32-i); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net or subnet, not a host. + */ +char * +netname(uint32_t in, uint32_t mask) +{ + char *cp = 0; + static char line[MAXHOSTNAMELEN]; + struct netent *np = 0; + uint32_t net, omask, dmask; + uint32_t i; + + i = ntohl(in); + dmask = forgemask(i); + omask = mask; + if (!nflag && i) { + net = i & dmask; + if (!(np = getnetbyaddr(i, AF_INET)) && net != i) + np = getnetbyaddr(net, AF_INET); + if (np) { + cp = np->n_name; + //### trimdomain(cp, strlen(cp)); + } + } + if (cp) + strlcpy(line, cp, sizeof(line)); + else { + switch (dmask) { + case IN_CLASSA_NET: + if ((i & IN_CLASSA_HOST) == 0) { + snprintf(line, sizeof(line), "%u", C(i >> 24)); + break; + } + /* FALLTHROUGH */ + case IN_CLASSB_NET: + if ((i & IN_CLASSB_HOST) == 0) { + snprintf(line, sizeof(line), "%u.%u", + C(i >> 24), C(i >> 16)); + break; + } + /* FALLTHROUGH */ + case IN_CLASSC_NET: + if ((i & IN_CLASSC_HOST) == 0) { + snprintf(line, sizeof(line), "%u.%u.%u", + C(i >> 24), C(i >> 16), C(i >> 8)); + break; + } + /* FALLTHROUGH */ + default: + snprintf(line, sizeof(line), "%u.%u.%u.%u", + C(i >> 24), C(i >> 16), C(i >> 8), C(i)); + break; + } + } + domask(line+strlen(line), i, omask); + return (line); +} + +#ifdef INET6 +char * +netname6(struct sockaddr_in6 *sa6, struct sockaddr *sam) +{ + static char line[MAXHOSTNAMELEN]; + u_char *lim; + int masklen, illegal = 0, flag = NI_WITHSCOPEID; + struct in6_addr *mask = sam ? &((struct sockaddr_in6 *)sam)->sin6_addr : 0; + + if (sam && sam->sa_len == 0) { + masklen = 0; + } else if (mask) { + u_char *p = (u_char *)mask; + for (masklen = 0, lim = p + 16; p < lim; p++) { + switch (*p) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + break; + case 0xfc: + masklen += 6; + break; + case 0xf8: + masklen += 5; + break; + case 0xf0: + masklen += 4; + break; + case 0xe0: + masklen += 3; + break; + case 0xc0: + masklen += 2; + break; + case 0x80: + masklen += 1; + break; + case 0x00: + break; + default: + illegal ++; + break; + } + } + if (illegal) + fprintf(stderr, "illegal prefixlen\n"); + } else { + masklen = 128; + } + if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr)) + return("default"); + + if (nflag) + flag |= NI_NUMERICHOST; + getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line), + NULL, 0, flag); + + if (nflag) + snprintf(&line[strlen(line)], sizeof(line) - strlen(line), "/%d", masklen); + + return line; +} + +char * +routename6(struct sockaddr_in6 *sa6) +{ + static char line[MAXHOSTNAMELEN]; + int flag = NI_WITHSCOPEID; + /* use local variable for safety */ + struct sockaddr_in6 sa6_local = {sizeof(sa6_local), AF_INET6, }; + + sa6_local.sin6_addr = sa6->sin6_addr; + sa6_local.sin6_scope_id = sa6->sin6_scope_id; + + if (nflag) + flag |= NI_NUMERICHOST; + + getnameinfo((struct sockaddr *)&sa6_local, sa6_local.sin6_len, + line, sizeof(line), NULL, 0, flag); + + return line; +} +#endif /*INET6*/ + +/* + * Print routing statistics + */ +void +rt_stats(void) +{ + struct rtstat rtstat; + int rttrash; + int mib[6]; + size_t len; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_STAT; + mib[5] = 0; + len = sizeof(struct rtstat); + if (sysctl(mib, 6, &rtstat, &len, 0, 0) == -1) + return; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_TRASH; + mib[5] = 0; + len = sizeof(rttrash); + if (sysctl(mib, 6, &rttrash, &len, 0, 0) == -1) + return; + + printf("routing:\n"); + +#define p(f, m) if (rtstat.f || sflag <= 1) \ + printf(m, rtstat.f, plural(rtstat.f)) + + p(rts_badredirect, "\t%u bad routing redirect%s\n"); + p(rts_dynamic, "\t%u dynamically created route%s\n"); + p(rts_newgateway, "\t%u new gateway%s due to redirects\n"); + p(rts_unreach, "\t%u destination%s found unreachable\n"); + p(rts_wildcard, "\t%u use%s of a wildcard route\n"); + p(rts_badrtgwroute, "\t%u lookup%s returned indirect " + "routes pointing to indirect gateway route\n"); +#undef p + + if (rttrash || sflag <= 1) + printf("\t%u route%s not in table but not freed\n", + rttrash, plural(rttrash)); +} + +void +upHex(char *p0) +{ + char *p = p0; + + for (; *p; p++) + switch (*p) { + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + *p += ('A' - 'a'); + break; + } +} diff --git a/network_cmds/netstat.tproj/systm.c b/network_cmds/netstat.tproj/systm.c new file mode 100644 index 0000000..3e076ca --- /dev/null +++ b/network_cmds/netstat.tproj/systm.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2014-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/*- + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/sys_domain.h> +#include <sys/kern_control.h> +#include <sys/kern_event.h> +#include <net/ntstat.h> + +#include <errno.h> +#include <err.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include "netstat.h" + +#define ROUNDUP64(a) \ +((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint64_t) - 1))) : sizeof(uint64_t)) +#define ADVANCE64(x, n) (((char *)x) += ROUNDUP64(n)) + +struct xgen_n { + u_int32_t xgn_len; /* length of this structure */ + u_int32_t xgn_kind; /* number of PCBs at this time */ +}; + +#define ALL_XGN_KIND_KCREG (XSO_KCREG) +#define ALL_XGN_KIND_EVT (XSO_SOCKET | XSO_RCVBUF | XSO_SNDBUF | XSO_STATS | XSO_EVT) +#define ALL_XGN_KIND_KCB (XSO_SOCKET | XSO_RCVBUF | XSO_SNDBUF | XSO_STATS | XSO_KCB) + +void +systmpr(uint32_t proto, + char *name, int af) +{ + const char *mibvar; + size_t len; + char *buf, *next; + struct xsystmgen *xig, *oxig; + struct xgen_n *xgn; + int which = 0; + struct xsocket_n *so = NULL; + struct xsockbuf_n *so_rcv = NULL; + struct xsockbuf_n *so_snd = NULL; + struct xsockstat_n *so_stat = NULL; + struct xkctl_reg *kctl = NULL; + struct xkctlpcb *kcb = NULL; + struct xkevtpcb *kevb = NULL; + int first = 1; + + switch (proto) { + case SYSPROTO_EVENT: + mibvar = "net.systm.kevt.pcblist"; + break; + case SYSPROTO_CONTROL: + mibvar = "net.systm.kctl.pcblist"; + break; + case 0: + mibvar = "net.systm.kctl.reg_list"; + break; + default: + mibvar = NULL; + break; + } + if (mibvar == NULL) + return; + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + /* + * Bail-out to avoid logic error in the loop below when + * there is in fact no more control block to process + */ + if (len <= sizeof(struct xsystmgen)) { + free(buf); + return; + } + oxig = xig = (struct xsystmgen *)buf; + for (next = buf + ROUNDUP64(xig->xg_len); next < buf + len; + next += ROUNDUP64(xgn->xgn_len)) { + xgn = (struct xgen_n*)next; + if (xgn->xgn_len <= sizeof(struct xsystmgen)) + break; + + if ((which & xgn->xgn_kind) == 0) { + which |= xgn->xgn_kind; + switch (xgn->xgn_kind) { + case XSO_SOCKET: + so = (struct xsocket_n *)xgn; + break; + case XSO_RCVBUF: + so_rcv = (struct xsockbuf_n *)xgn; + break; + case XSO_SNDBUF: + so_snd = (struct xsockbuf_n *)xgn; + break; + case XSO_STATS: + so_stat = (struct xsockstat_n *)xgn; + break; + case XSO_KCREG: + kctl = (struct xkctl_reg *)xgn; + break; + case XSO_KCB: + kcb = (struct xkctlpcb *)xgn; + break; + case XSO_EVT: + kevb = (struct xkevtpcb *)xgn; + break; + default: + printf("unexpected kind %d\n", xgn->xgn_kind); + break; + } + } else { + if (vflag) + printf("got %d twice\n", xgn->xgn_kind); + } + + if (which == ALL_XGN_KIND_KCREG) { + which = 0; + + if (first) { + printf("Registered kernel control modules\n"); + if (Aflag) + printf("%-16.16s ", "kctlref"); + printf("%-8.8s ", "id"); + if (Aflag) + printf("%-8.8s ", "unit"); + printf("%-8.8s ", "flags"); + printf("%-8.8s ", "pcbcount"); + printf("%-8.8s ", "rcvbuf"); + printf("%-8.8s ", "sndbuf"); + printf("%s ", "name"); + printf("\n"); + first = 0; + } + if (Aflag) + printf("%16llx ", kctl->xkr_kctlref); + printf("%8x ", kctl->xkr_id); + if (Aflag) + printf("%8d ", kctl->xkr_reg_unit); + printf("%8x ", kctl->xkr_flags); + printf("%8d ", kctl->xkr_pcbcount); + printf("%8d ", kctl->xkr_recvbufsize); + printf("%8d ", kctl->xkr_sendbufsize); + printf("%s ", kctl->xkr_name); + printf("\n"); + } else if (which == ALL_XGN_KIND_KCB) { + which = 0; + + if (first) { + printf("Active kernel control sockets\n"); + if (Aflag) + printf("%16.16s ", "pcb"); + printf("%-5.5s %-6.6s %-6.6s ", + "Proto", "Recv-Q", "Send-Q"); + if (bflag > 0) + printf("%10.10s %10.10s ", + "rxbytes", "txbytes"); + if (vflag > 0) + printf("%6.6s %6.6s %6.6s %6.6s ", + "rhiwat", "shiwat", "pid", "epid"); + printf("%6.6s ", "unit"); + printf("%6.6s ", "id"); + printf("%s", "name"); + printf("\n"); + first = 0; + } + if (Aflag) + printf("%16llx ", kcb->xkp_kctpcb); + printf("%-5.5s %6u %6u ", name, + so_rcv->sb_cc, + so_snd->sb_cc); + if (bflag > 0) { + int i; + u_int64_t rxbytes = 0; + u_int64_t txbytes = 0; + + for (i = 0; i < SO_TC_STATS_MAX; i++) { + rxbytes += so_stat->xst_tc_stats[i].rxbytes; + txbytes += so_stat->xst_tc_stats[i].txbytes; + } + printf("%10llu %10llu ", rxbytes, txbytes); + } + if (vflag > 0) { + printf("%6u %6u %6u %6u ", + so_rcv->sb_hiwat, + so_snd->sb_hiwat, + so->so_last_pid, + so->so_e_pid); + } + printf("%6d ", kcb->xkp_unit); + printf("%6d ", kcb->xkp_kctlid); + printf("%s", kcb->xkp_kctlname); + printf("\n"); + + } else if (which == ALL_XGN_KIND_EVT) { + which = 0; + if (first) { + printf("Active kernel event sockets\n"); + if (Aflag) + printf("%16.16s ", "pcb"); + printf("%-5.5s %-6.6s %-6.6s ", + "Proto", "Recv-Q", "Send-Q"); + printf("%6.6s ", "vendor"); + printf("%6.6s ", "class"); + printf("%6.6s", "subclass"); + if (bflag > 0) + printf("%10.10s %10.10s ", + "rxbytes", "txbytes"); + if (vflag > 0) + printf("%6.6s %6.6s %6.6s %6.6s", + "rhiwat", "shiwat", "pid", "epid"); + printf("\n"); + first = 0; + } + if (Aflag) + printf("%16llx ", kevb->kep_evtpcb); + printf("%-5.5s %6u %6u ", name, + so_rcv->sb_cc, + so_snd->sb_cc); + printf("%6d ", kevb->kep_vendor_code_filter); + printf("%6d ", kevb->kep_class_filter); + printf("%6d", kevb->kep_subclass_filter); + if (bflag > 0) { + int i; + u_int64_t rxbytes = 0; + u_int64_t txbytes = 0; + + for (i = 0; i < SO_TC_STATS_MAX; i++) { + rxbytes += so_stat->xst_tc_stats[i].rxbytes; + txbytes += so_stat->xst_tc_stats[i].txbytes; + } + printf("%10llu %10llu ", rxbytes, txbytes); + } + if (vflag > 0) { + printf("%6u %6u %6u %6u", + so_rcv->sb_hiwat, + so_snd->sb_hiwat, + so->so_last_pid, + so->so_e_pid); + } + printf("\n"); + } + + } + if (xig != oxig && xig->xg_gen != oxig->xg_gen) { + if (oxig->xg_count > xig->xg_count) { + printf("Some %s sockets may have been deleted.\n", + name); + } else if (oxig->xg_count < xig->xg_count) { + printf("Some %s sockets may have been created.\n", + name); + } else { + printf("Some %s sockets may have been created or deleted", + name); + } + } + free(buf); +} + +void +kctl_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct kctlstat pkctlstat; + struct kctlstat kctlstat; + size_t len = sizeof(struct kctlstat); + const char *mibvar = "net.systm.kctl.stats"; + + if (sysctlbyname(mibvar, &kctlstat, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define STATDIFF(f) (kctlstat.f - pkctlstat.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + p(kcs_reg_total, "\t%llu total kernel control module%s registered\n"); + p(kcs_reg_count, "\t%llu current kernel control module%s registered\n"); + p(kcs_pcbcount, "\t%llu current kernel control socket%s\n"); + p1a(kcs_gencnt, "\t%llu kernel control generation count\n"); + p(kcs_connections, "\t%llu connection attempt%s\n"); + p(kcs_conn_fail, "\t%llu connection failure%s\n"); + p(kcs_send_fail, "\t%llu send failure%s\n"); + p(kcs_send_list_fail, "\t%llu send list failure%s\n"); + p(kcs_enqueue_fail, "\t%llu enqueue failure%s\n"); + p(kcs_enqueue_fullsock, "\t%llu packet%s dropped due to full socket buffers\n"); + p(kcs_bad_kctlref, "\t%llu failure%s with bad kern_ctl_ref\n"); + p(kcs_tbl_size_too_big, "\t%llu register failure%s because of too many kern_ctl_ref\n"); + p(kcs_enqdata_mb_alloc_fail, "\t%llu enqueuedata failure%s because could not allocate a packet\n"); + p(kcs_enqdata_sbappend_fail, "\t%llu enqueuedata failure%s due to full socket buffers\n"); + +#undef STATDIFF +#undef p +#undef p1a + + if (interval > 0) + bcopy(&kctlstat, &pkctlstat, len); +} + +void +kevt_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct kevtstat pkevtstat; + struct kevtstat kevtstat; + size_t len = sizeof(struct kevtstat); + const char *mibvar = "net.systm.kevt.stats"; + + if (sysctlbyname(mibvar, &kevtstat, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define STATDIFF(f) (kevtstat.f - pkevtstat.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + p(kes_pcbcount, "\t%llu current kernel control socket%s\n"); + p1a(kes_gencnt, "\t%llu kernel control generation count\n"); + p(kes_badvendor, "\t%llu bad vendor failure%s\n"); + p(kes_toobig, "\t%llu message too big failure%s\n"); + p(kes_nomem, "\t%llu out of memory failure%s\n"); + p(kes_fullsock, "\t%llu message%s dropped due to full socket buffers\n"); + p(kes_posted, "\t%llu message%s posted\n"); + +#undef STATDIFF +#undef p +#undef p1a + + if (interval > 0) + bcopy(&kevtstat, &pkevtstat, len); +} + +void +print_extbkidle_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct soextbkidlestat psoextbkidlestat; + struct soextbkidlestat soextbkidlestat; + size_t len = sizeof(struct soextbkidlestat); + const char *mibvar = "kern.ipc.extbkidlestat"; + + if (sysctlbyname(mibvar, &soextbkidlestat, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + +#define STATDIFF(f) (soextbkidlestat.f - psoextbkidlestat.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + + p1a(so_xbkidle_maxperproc, "\t%u max per process\n"); + p1a(so_xbkidle_time, "\t%u maximum time (seconds)\n"); + p1a(so_xbkidle_rcvhiwat, "\t%u high water mark\n"); + p(so_xbkidle_notsupp, "\t%u socket option not supported failure%s\n"); + p(so_xbkidle_toomany, "\t%u too many sockets failure%s\n"); + p(so_xbkidle_wantok, "\t%u total socket%s requested OK\n"); + p(so_xbkidle_active, "\t%u extended bk idle socket%s\n"); + p(so_xbkidle_nocell, "\t%u no cellular failure%s\n"); + p(so_xbkidle_notime, "\t%u no time failures%s\n"); + p(so_xbkidle_forced, "\t%u forced defunct socket%s\n"); + p(so_xbkidle_resumed, "\t%u resumed socket%s\n"); + p(so_xbkidle_expired, "\t%u timeout expired failure%s\n"); + p1a(so_xbkidle_expired, "\t%u timer rescheduled\n"); + p(so_xbkidle_nodlgtd, "\t%u no delegated failure%s\n"); + +#undef STATDIFF +#undef p +#undef p1a +} + +void +print_nstat_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct nstat_stats pnstat_stats; + struct nstat_stats nstat_stats; + size_t len = sizeof(struct nstat_stats); + const char *mibvar = "net.stats.stats"; + + if (sysctlbyname(mibvar, &nstat_stats, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + +#define STATDIFF(f) (nstat_stats.f - pnstat_stats.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ +printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ +printf(m, STATDIFF(f)) + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + + p(nstat_successmsgfailures, "\t%u enqueue success message failure%s\n"); + p(nstat_sendcountfailures, "\t%u enqueue source counts message failure%s\n"); + p(nstat_sysinfofailures, "\t%u enqueue sysinfo message failure%s\n"); + p(nstat_srcupatefailures, "\t%u enqueue source udpate message failure%s\n"); + p(nstat_descriptionfailures, "\t%u enqueue description message failure%s\n"); + p(nstat_msgremovedfailures, "\t%u enqueue remove message failure%s\n"); + p(nstat_srcaddedfailures, "\t%u enqueue source added message failure%s\n"); + p(nstat_msgerrorfailures, "\t%u enqueue error message failure%s\n"); + p(nstat_copy_descriptor_failures, "\t%u copy descriptor failure%s\n"); + p(nstat_provider_counts_failures, "\t%u provider counts failure%s\n"); + p(nstat_control_send_description_failures, "\t%u control send description failure%s\n"); + p(nstat_control_send_goodbye_failures, "\t%u control send goodbye failure%s\n"); + p(nstat_flush_accumulated_msgs_failures, "\t%u flush accumulated messages failure%s\n"); + p(nstat_accumulate_msg_failures, "\t%u accumulated message failure%s\n"); + p(nstat_control_cleanup_source_failures, "\t%u control cleanup source failure%s\n"); + p(nstat_handle_msg_failures, "\t%u handle message failure%s\n"); + +#undef STATDIFF +#undef p +#undef p1a +} diff --git a/network_cmds/netstat.tproj/tp_astring.c b/network_cmds/netstat.tproj/tp_astring.c new file mode 100644 index 0000000..af08ceb --- /dev/null +++ b/network_cmds/netstat.tproj/tp_astring.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tp_astring.c 8.1 (Berkeley) 6/10/93 + */ + +char *tp_sstring[] = { +"ST_ERROR(0x0)", +"TP_CLOSED(0x1)", +"TP_CRSENT(0x2)", +"TP_AKWAIT(0x3)", +"TP_OPEN(0x4)", +"TP_CLOSING(0x5)", +"TP_REFWAIT(0x6)", +"TP_LISTENING(0x7)", +"TP_CONFIRMING(0x8)", +}; + +char *tp_estring[] = { +"TM_inact(0x0)", +"TM_retrans(0x1)", +"TM_sendack(0x2)", +"TM_notused(0x3)", +"TM_reference(0x4)", +"TM_data_retrans(0x5)", +"ER_TPDU(0x6)", +"CR_TPDU(0x7)", +"DR_TPDU(0x8)", +"DC_TPDU(0x9)", +"CC_TPDU(0xa)", +"AK_TPDU(0xb)", +"DT_TPDU(0xc)", +"XPD_TPDU(0xd)", +"XAK_TPDU(0xe)", +"T_CONN_req(0xf)", +"T_DISC_req(0x10)", +"T_LISTEN_req(0x11)", +"T_DATA_req(0x12)", +"T_XPD_req(0x13)", +"T_USR_rcvd(0x14)", +"T_USR_Xrcvd(0x15)", +"T_DETACH(0x16)", +"T_NETRESET(0x17)", +"T_ACPT_req(0x18)", +}; diff --git a/network_cmds/netstat.tproj/unix.c b/network_cmds/netstat.tproj/unix.c new file mode 100644 index 0000000..eece65a --- /dev/null +++ b/network_cmds/netstat.tproj/unix.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/*- + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Display protocol blocks in the unix domain. + */ +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> +#include <sys/un.h> +#include <sys/unpcb.h> + +#include <netinet/in.h> + +#include <errno.h> +#include <err.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include "netstat.h" + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +static void unixdomainpr __P((struct xunpcb64 *, struct xsocket64 *)); +#else +static void unixdomainpr __P((struct xunpcb *, struct xsocket *)); +#endif + +static const char *const socktype[] = + { "#0", "stream", "dgram", "raw" }; + +void +unixpr() +{ + char *buf; + int type; + size_t len; + struct xunpgen *xug, *oxug; +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + struct xsocket64 *so; + struct xunpcb64 *xunp; + char mibvar[sizeof "net.local.seqpacket.pcblist64"]; +#else + struct xsocket *so; + struct xunpcb *xunp; + char mibvar[sizeof "net.local.seqpacket.pcblist"]; +#endif + + for (type = SOCK_STREAM; type <= SOCK_RAW; type++) { +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + snprintf(mibvar, sizeof(mibvar), "net.local.%s.pcblist64", socktype[type]); +#else + snprintf(mibvar, sizeof(mibvar), "net.local.%s.pcblist", socktype[type]); +#endif + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + continue; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + oxug = xug = (struct xunpgen *)buf; + for (xug = (struct xunpgen *)((char *)xug + xug->xug_len); + xug->xug_len > sizeof(struct xunpgen); + xug = (struct xunpgen *)((char *)xug + xug->xug_len)) { +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + xunp = (struct xunpcb64 *)xug; +#else + xunp = (struct xunpcb *)xug; +#endif + so = &xunp->xu_socket; + + /* Ignore PCBs which were freed during copyout. */ +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + if (xunp->xunp_gencnt > oxug->xug_gen) +#else + if (xunp->xu_unp.unp_gencnt > oxug->xug_gen) +#endif + continue; + unixdomainpr(xunp, so); + } + if (xug != oxug && xug->xug_gen != oxug->xug_gen) { + if (oxug->xug_count > xug->xug_count) { + printf("Some %s sockets may have been deleted.\n", + socktype[type]); + } else if (oxug->xug_count < xug->xug_count) { + printf("Some %s sockets may have been created.\n", + socktype[type]); + } else { + printf("Some %s sockets may have been created or deleted\n", + socktype[type]); + } + } + free(buf); + } +} + +static void +unixdomainpr(xunp, so) +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + struct xunpcb64 *xunp; + struct xsocket64 *so; +#else + struct xunpcb *xunp; + struct xsocket *so; +#endif +{ +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + struct unpcb *unp; +#endif + struct sockaddr_un *sa; + static int first = 1; + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + sa = &xunp->xu_addr; +#else + unp = &xunp->xu_unp; + if (unp->unp_addr) + sa = &xunp->xu_addr; + else + sa = (struct sockaddr_un *)0; +#endif + + if (first) { + printf("Active LOCAL (UNIX) domain sockets\n"); + printf( +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +"%-16.16s %-6.6s %-6.6s %-6.6s %16.16s %16.16s %16.16s %16.16s Addr\n", +#else +"%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s Addr\n", +#endif + "Address", "Type", "Recv-Q", "Send-Q", + "Inode", "Conn", "Refs", "Nextref"); + first = 0; + } +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + printf("%16lx %-6.6s %6u %6u %16lx %16lx %16lx %16lx", + (long)xunp->xu_unpp, socktype[so->so_type], so->so_rcv.sb_cc, + so->so_snd.sb_cc, + (long)xunp->xunp_vnode, (long)xunp->xunp_conn, + (long)xunp->xunp_refs, (long)xunp->xunp_reflink.le_next); +#else + printf("%8lx %-6.6s %6u %6u %8lx %8lx %8lx %8lx", + (long)so->so_pcb, socktype[so->so_type], so->so_rcv.sb_cc, + so->so_snd.sb_cc, + (long)unp->unp_vnode, (long)unp->unp_conn, + (long)unp->unp_refs.lh_first, (long)unp->unp_reflink.le_next); +#endif + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + if (sa->sun_len) +#else + if (sa) +#endif + printf(" %.*s", + (int)(sa->sun_len - offsetof(struct sockaddr_un, sun_path)), + sa->sun_path); + putchar('\n'); +} diff --git a/network_cmds/netstat.tproj/vsock.c b/network_cmds/netstat.tproj/vsock.c new file mode 100644 index 0000000..b52dc5e --- /dev/null +++ b/network_cmds/netstat.tproj/vsock.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Display protocol blocks in the vsock domain. + */ +#include <sys/proc_info.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/vsock.h> + +#include <errno.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include "netstat.h" + +#ifdef AF_VSOCK + +static void vsockdomainpr __P((struct xvsockpcb *)); + +void +vsockpr(uint32_t proto, +char *name, int af) +{ + char *buf, *next; + size_t len; + struct xvsockpgen *xvg, *oxvg; + struct xvsockpcb *xpcb; + + const char* mibvar = "net.vsock.pcblist"; + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + /* + * Bail-out to avoid logic error in the loop below when + * there is in fact no more control block to process + */ + if (len <= 2 * sizeof(struct xvsockpgen)) { + free(buf); + return; + } + + oxvg = (struct xvsockpgen *)buf; + + // Save room for the last xvsockpgen. + len -= oxvg->xvg_len; + + for (next = buf + oxvg->xvg_len; next < buf + len; next += xpcb->xv_len) { + xpcb = (struct xvsockpcb *)next; + + /* Ignore PCBs which were freed during copyout. */ + if (xpcb->xvp_gencnt > oxvg->xvg_gen) + continue; + vsockdomainpr(xpcb); + } + xvg = (struct xvsockpgen *)next; + if (xvg != oxvg && xvg->xvg_gen != oxvg->xvg_gen) { + if (oxvg->xvg_count > xvg->xvg_count) { + printf("Some vsock sockets may have been deleted.\n"); + } else if (oxvg->xvg_count < xvg->xvg_count) { + printf("Some vsock sockets may have been created.\n"); + } else { + printf("Some vsock sockets may have been created or deleted.\n"); + } + } + free(buf); +} + +static void +vsock_print_addr(buf, cid, port) + char *buf; + uint32_t cid; + uint32_t port; +{ + if (cid == VMADDR_CID_ANY && port == VMADDR_PORT_ANY) { + (void) sprintf(buf, "*:*"); + } else if (cid == VMADDR_CID_ANY) { + (void) sprintf(buf, "*:%u", port); + } else if (port == VMADDR_PORT_ANY) { + (void) sprintf(buf, "%u:*", cid); + } else { + (void) sprintf(buf, "%u:%u", cid, port); + } +} + +static void +vsockdomainpr(xpcb) + struct xvsockpcb *xpcb; +{ + static int first = 1; + + if (first) { + printf("Active VSock sockets\n"); + printf("%-5.5s %-6.6s %-6.6s %-6.6s %-18.18s %-18.18s %-11.11s", + "Proto", "Type", + "Recv-Q", "Send-Q", + "Local Address", "Foreign Address", + "State"); + if (vflag > 0) + printf(" %10.10s %10.10s %10.10s %10.10s %6.6s %6.6s %6.6s %6s %10s", + "rxcnt", "txcnt", "peer_rxcnt", "peer_rxhiwat", + "rxhiwat", "txhiwat", "pid", "state", "options"); + printf("\n"); + first = 0; + } + + struct xsocket *so = &xpcb->xv_socket; + + char srcAddr[50]; + char dstAddr[50]; + + vsock_print_addr(srcAddr, xpcb->xvp_local_cid, xpcb->xvp_local_port); + vsock_print_addr(dstAddr, xpcb->xvp_remote_cid, xpcb->xvp_remote_port); + + // Determine the vsock socket state. + char *state; + if (so->so_state & SOI_S_ISCONNECTING) { + state = "CONNECTING"; + } else if (so->so_state & SOI_S_ISCONNECTED) { + state = "ESTABLISHED"; + } else if (so->so_state & SOI_S_ISDISCONNECTING) { + state = "CLOSING"; + } else if (so->so_options & SO_ACCEPTCONN) { + state = "LISTEN"; + } else { + state = "CLOSED"; + } + + printf("%-5.5s %-6.6s %6u %6u %-18s %-18s %-11s", + "vsock", "stream", + so->so_rcv.sb_cc, so->so_snd.sb_cc, + srcAddr, dstAddr, + state); + if (vflag > 0) + printf(" %10u %10u %10u %10u %6u %6u %6u 0x%04x 0x%08x", + xpcb->xvp_rxcnt, + xpcb->xvp_txcnt, + xpcb->xvp_peer_rxcnt, + xpcb->xvp_peer_rxhiwat, + so->so_rcv.sb_hiwat, + so->so_snd.sb_hiwat, + xpcb->xvp_last_pid, + so->so_state, + so->so_options); + printf("\n"); +} + +#endif /* AF_VSOCK */ diff --git a/network_cmds/network-client-server-entitlements.plist b/network_cmds/network-client-server-entitlements.plist new file mode 100644 index 0000000..c326c83 --- /dev/null +++ b/network_cmds/network-client-server-entitlements.plist @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.network.client</key> + <true/> + <key>com.apple.security.network.server</key> + <true/> +</dict> +</plist> diff --git a/network_cmds/network_cmds.plist b/network_cmds/network_cmds.plist new file mode 100644 index 0000000..9d6cf73 --- /dev/null +++ b/network_cmds/network_cmds.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<array> + <dict> + <key>OpenSourceProject</key> + <string>mtest</string> + <key>OpenSourceVersion</key> + <string>1.11</string> + <key>OpenSourceWebsiteURL</key> + <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.sbin/mtest/</string> + <key>OpenSourceURL</key> + <string>http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/usr.sbin/mtest/mtest.c?rev=1.11;content-type=text%2Fplain</string> + <key>OpenSourceSHA1</key> + <string></string> + <key>OpenSourceMD5</key> + <string></string> + <key>OpenSourceImportDate</key> + <string>2011-01-13</string> + <key>OpenSourceLicense</key> + <string>Other</string> + <key>OpenSourceLicenseFile</key> + <string>network_cmds.txt</string> + </dict> +</array> +</plist> diff --git a/network_cmds/network_cmds.xcodeproj/project.pbxproj b/network_cmds/network_cmds.xcodeproj/project.pbxproj new file mode 100755 index 0000000..cce64f8 --- /dev/null +++ b/network_cmds/network_cmds.xcodeproj/project.pbxproj @@ -0,0 +1,5098 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 72570DA20EE8EBF3000F4CFB /* All-Embedded */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 72570DA60EE8EC0F000F4CFB /* Build configuration list for PBXAggregateTarget "All-Embedded" */; + buildPhases = ( + ); + dependencies = ( + 034E4464100BDCA3009CA3DC /* PBXTargetDependency */, + 72B732EB1899B19A0060E6D4 /* PBXTargetDependency */, + 72179EAE146233390098FB3E /* PBXTargetDependency */, + 7282BA571AFBDEAD005DE836 /* PBXTargetDependency */, + 72946F4C1BBF063800087E35 /* PBXTargetDependency */, + 034E4469100BDD00009CA3DC /* PBXTargetDependency */, + 565825AF13392239003E5FA5 /* PBXTargetDependency */, + 72311F4D194A34F500EB4788 /* PBXTargetDependency */, + 690D97BC12DE7151004323A7 /* PBXTargetDependency */, + 03B2DBD1100BE626005349BC /* PBXTargetDependency */, + 034E4475100BDEC6009CA3DC /* PBXTargetDependency */, + 7250E1491616642900A11A76 /* PBXTargetDependency */, + 7200F3051958A4FA0033E22C /* PBXTargetDependency */, + 034E447B100BDF0D009CA3DC /* PBXTargetDependency */, + 03B2DBD3100BE645005349BC /* PBXTargetDependency */, + 18515B85133D1DBF000148A4 /* PBXTargetDependency */, + 034E447F100BDF54009CA3DC /* PBXTargetDependency */, + 03B2DBDB100BE6D2005349BC /* PBXTargetDependency */, + 034E4485100BE15F009CA3DC /* PBXTargetDependency */, + 03B2DBDD100BE6D5005349BC /* PBXTargetDependency */, + ); + name = "All-Embedded"; + productName = "All-Embedded"; + }; + 726121430EE8717500AFED1B /* All */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 7261214B0EE8717D00AFED1B /* Build configuration list for PBXAggregateTarget "All" */; + buildPhases = ( + ); + dependencies = ( + 726121490EE8717B00AFED1B /* PBXTargetDependency */, + 72B732E91899B18F0060E6D4 /* PBXTargetDependency */, + 723C7074142BB003007C87E9 /* PBXTargetDependency */, + 7282BA5B1AFBDED3005DE836 /* PBXTargetDependency */, + 7263724B1BCC709900E4B026 /* PBXTargetDependency */, + 7261217D0EE8896800AFED1B /* PBXTargetDependency */, + 4D2B05141208C6BB0004A3F3 /* PBXTargetDependency */, + 724DABC30EE890A6008900D0 /* PBXTargetDependency */, + 565825AD13392232003E5FA5 /* PBXTargetDependency */, + 72311F4B194A34EB00EB4788 /* PBXTargetDependency */, + 690D97BE12DE7166004323A7 /* PBXTargetDependency */, + 724DAC240EE89525008900D0 /* PBXTargetDependency */, + 7216D2670EE8978F00AE70E4 /* PBXTargetDependency */, + 7250E1471616642000A11A76 /* PBXTargetDependency */, + 7200F3031958A4F10033E22C /* PBXTargetDependency */, + 7216D2C00EE89ADF00AE70E4 /* PBXTargetDependency */, + 7216D2C20EE89ADF00AE70E4 /* PBXTargetDependency */, + 7216D2DA0EE89BE900AE70E4 /* PBXTargetDependency */, + 7216D3060EE89D9A00AE70E4 /* PBXTargetDependency */, + 7216D34D0EE89FEC00AE70E4 /* PBXTargetDependency */, + 7216D37F0EE8A0B300AE70E4 /* PBXTargetDependency */, + 7294F0EA0EE8BAC80052EC88 /* PBXTargetDependency */, + 7294F1210EE8BCC20052EC88 /* PBXTargetDependency */, + 72CD1D9C0EE8C47C005F825D /* PBXTargetDependency */, + 71D958C51A9455A000C9B286 /* PBXTargetDependency */, + ); + name = All; + productName = "network_cmds (Aggregate)"; + }; + 72ABD0811083D742008C721C /* All-EmbeddedOther */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 72ABD0A11083D792008C721C /* Build configuration list for PBXAggregateTarget "All-EmbeddedOther" */; + buildPhases = ( + ); + dependencies = ( + 72ABD0A41083D818008C721C /* PBXTargetDependency */, + 72ABD0881083D750008C721C /* PBXTargetDependency */, + 7282BA591AFBDEC2005DE836 /* PBXTargetDependency */, + 72946F4E1BBF063F00087E35 /* PBXTargetDependency */, + 565825B113392242003E5FA5 /* PBXTargetDependency */, + 72311F4F194A34FE00EB4788 /* PBXTargetDependency */, + 690D97BA12DE7130004323A7 /* PBXTargetDependency */, + 72ABD08C1083D75D008C721C /* PBXTargetDependency */, + 72ABD08E1083D75F008C721C /* PBXTargetDependency */, + 7250E14B1616643000A11A76 /* PBXTargetDependency */, + 7200F3071958A5040033E22C /* PBXTargetDependency */, + 72ABD0901083D762008C721C /* PBXTargetDependency */, + 72ABD0921083D764008C721C /* PBXTargetDependency */, + 72ABD0941083D767008C721C /* PBXTargetDependency */, + 72ABD0961083D76A008C721C /* PBXTargetDependency */, + 72ABD0981083D76D008C721C /* PBXTargetDependency */, + 72ABD09A1083D76F008C721C /* PBXTargetDependency */, + 72ABD09C1083D772008C721C /* PBXTargetDependency */, + 72ABD09E1083D774008C721C /* PBXTargetDependency */, + ); + name = "All-EmbeddedOther"; + productName = "All-EmbeddedOther"; + }; + 72C77D3A1484199C002D2577 /* network_cmds_libs */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 72C77D671484199C002D2577 /* Build configuration list for PBXAggregateTarget "network_cmds_libs" */; + buildPhases = ( + ); + dependencies = ( + ); + name = network_cmds_libs; + productName = "network_cmds (Aggregate)"; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 01560DFD241969D3001AA29A /* vsock.c in Sources */ = {isa = PBXBuildFile; fileRef = 01560DFC241969D3001AA29A /* vsock.c */; }; + 03EB2F9A120A1DDA0007C1A0 /* ip6addrctl.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4D2B04E41208C12F0004A3F3 /* ip6addrctl.8 */; }; + 4D2B04F81208C21B0004A3F3 /* ip6addrctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D2B04E51208C12F0004A3F3 /* ip6addrctl.c */; }; + 565825A4133921A3003E5FA5 /* mnc_error.c in Sources */ = {isa = PBXBuildFile; fileRef = 565825961339217B003E5FA5 /* mnc_error.c */; }; + 565825A5133921A3003E5FA5 /* mnc_main.c in Sources */ = {isa = PBXBuildFile; fileRef = 565825971339217B003E5FA5 /* mnc_main.c */; }; + 565825A6133921A3003E5FA5 /* mnc_multicast.c in Sources */ = {isa = PBXBuildFile; fileRef = 565825981339217B003E5FA5 /* mnc_multicast.c */; }; + 565825A7133921A3003E5FA5 /* mnc_opts.c in Sources */ = {isa = PBXBuildFile; fileRef = 565825991339217B003E5FA5 /* mnc_opts.c */; }; + 565825A9133921CF003E5FA5 /* mnc.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 565825941339217B003E5FA5 /* mnc.1 */; }; + 56B6B66816F79A1C00D8A7A9 /* mptcp.c in Sources */ = {isa = PBXBuildFile; fileRef = 56B6B66716F79A1C00D8A7A9 /* mptcp.c */; }; + 690D97A612DE6F96004323A7 /* mtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 690D979412DE6E6B004323A7 /* mtest.c */; }; + 690D97AE12DE70AE004323A7 /* mtest.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 690D979512DE6E76004323A7 /* mtest.8 */; }; + 7200F2FD1958A34D0033E22C /* packet_mangler.c in Sources */ = {isa = PBXBuildFile; fileRef = 7200F2FC1958A34D0033E22C /* packet_mangler.c */; }; + 721654C31EC52447005B17BA /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 721654C21EC52447005B17BA /* misc.c */; }; + 7216D24C0EE896F300AE70E4 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261208B0EE86F4800AFED1B /* data.c */; }; + 7216D24D0EE896F300AE70E4 /* if.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261208D0EE86F4800AFED1B /* if.c */; }; + 7216D24E0EE896F300AE70E4 /* inet.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261208E0EE86F4800AFED1B /* inet.c */; }; + 7216D24F0EE896F300AE70E4 /* inet6.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261208F0EE86F4800AFED1B /* inet6.c */; }; + 7216D2500EE896F300AE70E4 /* ipsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120900EE86F4800AFED1B /* ipsec.c */; }; + 7216D2510EE896F300AE70E4 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120910EE86F4800AFED1B /* main.c */; }; + 7216D2520EE896F300AE70E4 /* mbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120930EE86F4800AFED1B /* mbuf.c */; }; + 7216D2530EE896F300AE70E4 /* mcast.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120940EE86F4800AFED1B /* mcast.c */; }; + 7216D2560EE896F300AE70E4 /* route.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120990EE86F4800AFED1B /* route.c */; }; + 7216D2570EE896F300AE70E4 /* tp_astring.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261209A0EE86F4800AFED1B /* tp_astring.c */; }; + 7216D2580EE896F300AE70E4 /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261209B0EE86F4800AFED1B /* unix.c */; }; + 7216D2600EE8971500AE70E4 /* netstat.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120970EE86F4800AFED1B /* netstat.1 */; }; + 7216D2800EE8981C00AE70E4 /* ping.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120A10EE86F5000AFED1B /* ping.c */; }; + 7216D2880EE8982F00AE70E4 /* ping.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120A00EE86F5000AFED1B /* ping.8 */; }; + 7216D2A00EE898DF00AE70E4 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120A60EE86F5C00AFED1B /* md5.c */; }; + 7216D2A10EE898DF00AE70E4 /* ping6.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120A90EE86F5C00AFED1B /* ping6.c */; }; + 7216D2A90EE898F300AE70E4 /* ping6.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120A80EE86F5C00AFED1B /* ping6.8 */; }; + 7216D2D10EE89B8300AE70E4 /* rarpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120AF0EE86F6700AFED1B /* rarpd.c */; }; + 7216D2E40EE89C8B00AE70E4 /* rarpd.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120AE0EE86F6700AFED1B /* rarpd.8 */; }; + 7216D2F20EE89CD600AE70E4 /* route.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120B80EE86F7200AFED1B /* route.c */; }; + 7216D3040EE89D4900AE70E4 /* route.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120B70EE86F7200AFED1B /* route.8 */; }; + 7216D3190EE89EC100AE70E4 /* advcap.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120BC0EE86F8200AFED1B /* advcap.c */; }; + 7216D31A0EE89EC100AE70E4 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120BE0EE86F8200AFED1B /* config.c */; }; + 7216D31B0EE89EC100AE70E4 /* dump.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120C00EE86F8200AFED1B /* dump.c */; }; + 7216D31C0EE89EC100AE70E4 /* if.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120C20EE86F8200AFED1B /* if.c */; }; + 7216D31D0EE89EC100AE70E4 /* rrenum.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120C60EE86F8200AFED1B /* rrenum.c */; }; + 7216D31E0EE89EC100AE70E4 /* rtadvd.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120C90EE86F8200AFED1B /* rtadvd.c */; }; + 7216D31F0EE89EC100AE70E4 /* timer.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120CD0EE86F8200AFED1B /* timer.c */; }; + 7216D3240EE89F4200AE70E4 /* rtadvd.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120C80EE86F8200AFED1B /* rtadvd.8 */; }; + 7216D3390EE89F8400AE70E4 /* rtadvd.conf.5 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120CB0EE86F8200AFED1B /* rtadvd.conf.5 */; }; + 7216D3410EE89FAF00AE70E4 /* rtadvd.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120CA0EE86F8200AFED1B /* rtadvd.conf */; }; + 7216D3670EE8A04700AE70E4 /* dump.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120D20EE86F9100AFED1B /* dump.c */; }; + 7216D3680EE8A04700AE70E4 /* if.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120D30EE86F9100AFED1B /* if.c */; }; + 7216D3690EE8A04700AE70E4 /* probe.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120D50EE86F9100AFED1B /* probe.c */; }; + 7216D36A0EE8A04700AE70E4 /* rtsock.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120D60EE86F9100AFED1B /* rtsock.c */; }; + 7216D36B0EE8A04700AE70E4 /* rtsol.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120D80EE86F9100AFED1B /* rtsol.c */; }; + 7216D36C0EE8A04700AE70E4 /* rtsold.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120D90EE86F9100AFED1B /* rtsold.c */; }; + 7216D3700EE8A05B00AE70E4 /* rtsol.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120D70EE86F9100AFED1B /* rtsol.8 */; }; + 7216D3AB0EE8A3C400AE70E4 /* spray.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120E00EE86F9D00AFED1B /* spray.c */; }; + 7216D3AF0EE8A3D800AE70E4 /* spray.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120DF0EE86F9D00AFED1B /* spray.8 */; }; + 7218B54A191D4202001B7B52 /* systm.c in Sources */ = {isa = PBXBuildFile; fileRef = 7218B549191D4202001B7B52 /* systm.c */; }; + 72311F54194A354F00EB4788 /* conn_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 72311F50194A354F00EB4788 /* conn_lib.c */; }; + 72311F55194A354F00EB4788 /* mptcp_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 72311F53194A354F00EB4788 /* mptcp_client.c */; }; + 72311F56194A76DA00EB4788 /* mptcp_client.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 72311F52194A354F00EB4788 /* mptcp_client.1 */; }; + 724753E7144905E300F6A941 /* dnctl.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 724753E61448E1EF00F6A941 /* dnctl.8 */; }; + 724769371CE2B1FF00AAB5F0 /* gmt2local.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA3C1AFAD58E005DE836 /* gmt2local.c */; }; + 724769381CE2C1E400AAB5F0 /* gmt2local.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA3C1AFAD58E005DE836 /* gmt2local.c */; }; + 7247B83616165EDC00873B3C /* pktapctl.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7247B83516165EDC00873B3C /* pktapctl.8 */; }; + 7247B83C16165F0100873B3C /* pktapctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 7247B83B16165F0100873B3C /* pktapctl.c */; }; + 724DABA60EE88FED008900D0 /* kdumpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120710EE86F2D00AFED1B /* kdumpd.c */; }; + 724DABA70EE88FED008900D0 /* kdumpsubs.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120720EE86F2D00AFED1B /* kdumpsubs.c */; }; + 724DABAB0EE89006008900D0 /* kdumpd.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120700EE86F2D00AFED1B /* kdumpd.8 */; }; + 724DABBC0EE8908A008900D0 /* com.apple.kdumpd.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7261206E0EE86F2D00AFED1B /* com.apple.kdumpd.plist */; }; + 724DAC120EE89423008900D0 /* ndp.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120870EE86F4000AFED1B /* ndp.c */; }; + 724DAC3B0EE89555008900D0 /* ndp.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120860EE86F4000AFED1B /* ndp.8 */; }; + 726121310EE8711E00AFED1B /* arp.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261204E0EE86EF900AFED1B /* arp.c */; }; + 726121350EE8713800AFED1B /* arp.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7261204D0EE86EF900AFED1B /* arp.8 */; }; + 7261215A0EE8883900AFED1B /* ifbond.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120550EE86F0900AFED1B /* ifbond.c */; }; + 7261215B0EE8883900AFED1B /* ifconfig.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120570EE86F0900AFED1B /* ifconfig.c */; }; + 7261215C0EE8883900AFED1B /* ifmedia.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120590EE86F0900AFED1B /* ifmedia.c */; }; + 7261215D0EE8883900AFED1B /* ifvlan.c in Sources */ = {isa = PBXBuildFile; fileRef = 7261205A0EE86F0900AFED1B /* ifvlan.c */; }; + 726121610EE8885400AFED1B /* ifconfig.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120560EE86F0900AFED1B /* ifconfig.8 */; }; + 7263A9630EEE31C800164D5D /* libipsec.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72CD1DB50EE8C619005F825D /* libipsec.dylib */; }; + 7282BA491AFAD58E005DE836 /* capture.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA351AFAD58E005DE836 /* capture.c */; }; + 7282BA4B1AFAD58E005DE836 /* ecn_probe.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA391AFAD58E005DE836 /* ecn_probe.c */; }; + 7282BA4C1AFAD58E005DE836 /* ecn.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA3A1AFAD58E005DE836 /* ecn.c */; }; + 7282BA4D1AFAD58E005DE836 /* gmt2local.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA3C1AFAD58E005DE836 /* gmt2local.c */; }; + 7282BA4E1AFAD58E005DE836 /* history.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA3E1AFAD58E005DE836 /* history.c */; }; + 7282BA4F1AFAD58E005DE836 /* inet.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA401AFAD58E005DE836 /* inet.c */; }; + 7282BA521AFAD58E005DE836 /* session.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA451AFAD58E005DE836 /* session.c */; }; + 7282BA531AFAD58E005DE836 /* support.c in Sources */ = {isa = PBXBuildFile; fileRef = 7282BA471AFAD58E005DE836 /* support.c */; }; + 7282BA551AFBCA66005DE836 /* libpcap.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7282BA541AFBCA66005DE836 /* libpcap.dylib */; }; + 72946F501BBF07FF00087E35 /* frame_delay.c in Sources */ = {isa = PBXBuildFile; fileRef = 72946F4F1BBF07FF00087E35 /* frame_delay.c */; }; + 7294F0DF0EE8BA730052EC88 /* spray.x in Sources */ = {isa = PBXBuildFile; fileRef = 726120E10EE86F9D00AFED1B /* spray.x */; }; + 7294F1000EE8BB990052EC88 /* as.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120E50EE86FA700AFED1B /* as.c */; }; + 7294F1010EE8BB990052EC88 /* findsaddr-socket.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120E70EE86FA700AFED1B /* findsaddr-socket.c */; }; + 7294F1020EE8BB990052EC88 /* ifaddrlist.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120EA0EE86FA700AFED1B /* ifaddrlist.c */; }; + 7294F1030EE8BB990052EC88 /* traceroute.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120F10EE86FA700AFED1B /* traceroute.c */; }; + 7294F1040EE8BB990052EC88 /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120F30EE86FA700AFED1B /* version.c */; }; + 7294F1080EE8BBB10052EC88 /* traceroute.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120F00EE86FA700AFED1B /* traceroute.8 */; }; + 7294F12E0EE8BD2F0052EC88 /* traceroute6.c in Sources */ = {isa = PBXBuildFile; fileRef = 726120FB0EE86FB500AFED1B /* traceroute6.c */; }; + 7294F1320EE8BD430052EC88 /* traceroute6.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 726120FA0EE86FB500AFED1B /* traceroute6.8 */; }; + 72B732DF1899B0380060E6D4 /* cfilutil.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 72B732DE1899B0380060E6D4 /* cfilutil.1 */; }; + 72B732EF1899B23A0060E6D4 /* cfilutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 72B732EE1899B23A0060E6D4 /* cfilutil.c */; }; + 72B732F11899B2430060E6D4 /* cfilstat.c in Sources */ = {isa = PBXBuildFile; fileRef = 72B732F01899B2430060E6D4 /* cfilstat.c */; }; + 72B7F36B1BA69352003A9AA2 /* if6lowpan.c in Sources */ = {isa = PBXBuildFile; fileRef = 72B7F36A1BA69281003A9AA2 /* if6lowpan.c */; }; + 72B894EC0EEDB17C00C218D6 /* libipsec.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72CD1DB50EE8C619005F825D /* libipsec.dylib */; }; + 72D000C4142BB11100151981 /* dnctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 72D000C3142BB11100151981 /* dnctl.c */; }; + 72D33F572271319400EF5B5E /* rtadvd_logging.c in Sources */ = {isa = PBXBuildFile; fileRef = 72D33F562271220100EF5B5E /* rtadvd_logging.c */; }; + 72E42BA314B7CF3D003AAE28 /* network_cmds.plist in Install OSS Plist */ = {isa = PBXBuildFile; fileRef = 72E42BA214B7CF37003AAE28 /* network_cmds.plist */; }; + 72E650A7107BF2F000AAF325 /* af_inet.c in Sources */ = {isa = PBXBuildFile; fileRef = 72E650A2107BF2F000AAF325 /* af_inet.c */; }; + 72E650A8107BF2F000AAF325 /* af_inet6.c in Sources */ = {isa = PBXBuildFile; fileRef = 72E650A3107BF2F000AAF325 /* af_inet6.c */; }; + 72E650A9107BF2F000AAF325 /* af_link.c in Sources */ = {isa = PBXBuildFile; fileRef = 72E650A4107BF2F000AAF325 /* af_link.c */; }; + 72E650AA107BF2F000AAF325 /* ifbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 72E650A5107BF2F000AAF325 /* ifbridge.c */; }; + 72E650AB107BF2F000AAF325 /* ifclone.c in Sources */ = {isa = PBXBuildFile; fileRef = 72E650A6107BF2F000AAF325 /* ifclone.c */; }; + E01AB0901368880F008C66FF /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E01AB08F1368880F008C66FF /* libutil.dylib */; }; + F940359D1E2FF5A900283EB1 /* iffake.c in Sources */ = {isa = PBXBuildFile; fileRef = F940359C1E2FF58500283EB1 /* iffake.c */; }; + F97F1E041E9C3FC8002355FF /* nexus.c in Sources */ = {isa = PBXBuildFile; fileRef = F97F1E031E9C3FBC002355FF /* nexus.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 7216D47D0EE8B84900AE70E4 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + filePatterns = "$(PROJECT_DIR)/spray.tproj/spray.x"; + fileType = pattern.proxy; + inputFiles = ( + ); + isEditable = 1; + outputFiles = ( + "$(DERIVED_FILES_DIR)/$(INPUT_FILE_BASE).h", + "$(DERIVED_FILES_DIR)/$(INPUT_FILE_BASE)_xdr.c", + ); + script = "/usr/bin/rpcgen -h -o ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.h ${INPUT_FILE_PATH}\n/usr/bin/rpcgen -c -o ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}_xdr.c ${INPUT_FILE_PATH}"; + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 034E4463100BDCA3009CA3DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7261212C0EE8710B00AFED1B; + remoteInfo = arp; + }; + 034E4468100BDD00009CA3DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 726121530EE8881700AFED1B; + remoteInfo = ifconfig; + }; + 034E4474100BDEC6009CA3DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2450EE896C000AE70E4; + remoteInfo = netstat; + }; + 034E447A100BDF0D009CA3DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D27B0EE8980A00AE70E4; + remoteInfo = ping; + }; + 034E447E100BDF54009CA3DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2EC0EE89CBC00AE70E4; + remoteInfo = route; + }; + 034E4484100BE15F009CA3DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7294F0F80EE8BB460052EC88; + remoteInfo = traceroute; + }; + 03B2DBD0100BE626005349BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 724DAC0C0EE8940D008900D0; + remoteInfo = ndp; + }; + 03B2DBD2100BE645005349BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2990EE898BD00AE70E4; + remoteInfo = ping6; + }; + 03B2DBDA100BE6D2005349BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3580EE8A02200AE70E4; + remoteInfo = rtsol; + }; + 03B2DBDC100BE6D5005349BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7294F1290EE8BD280052EC88; + remoteInfo = traceroute6; + }; + 18515B84133D1DBF000148A4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3130EE89E9E00AE70E4; + remoteInfo = rtadvd; + }; + 4D2B05131208C6BB0004A3F3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4D2B04F21208C2040004A3F3; + remoteInfo = ip6addrctl; + }; + 565825AC13392232003E5FA5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5658259E1339218F003E5FA5; + remoteInfo = mnc; + }; + 565825AE13392239003E5FA5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5658259E1339218F003E5FA5; + remoteInfo = mnc; + }; + 565825B013392242003E5FA5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5658259E1339218F003E5FA5; + remoteInfo = mnc; + }; + 690D97B912DE7130004323A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 690D978012DE6034004323A7; + remoteInfo = mtest; + }; + 690D97BB12DE7151004323A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 690D978012DE6034004323A7; + remoteInfo = mtest; + }; + 690D97BD12DE7166004323A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 690D978012DE6034004323A7; + remoteInfo = mtest; + }; + 71D958C41A9455A000C9B286 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 713297671A93C743002359CF; + remoteInfo = unbound; + }; + 7200F3021958A4F10033E22C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7200F2F91958A34D0033E22C; + remoteInfo = pktmnglr; + }; + 7200F3041958A4FA0033E22C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7200F2F91958A34D0033E22C; + remoteInfo = pktmnglr; + }; + 7200F3061958A5040033E22C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7200F2F91958A34D0033E22C; + remoteInfo = pktmnglr; + }; + 7216D2660EE8978F00AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2450EE896C000AE70E4; + remoteInfo = netstat; + }; + 7216D2BF0EE89ADF00AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D27B0EE8980A00AE70E4; + remoteInfo = ping; + }; + 7216D2C10EE89ADF00AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2990EE898BD00AE70E4; + remoteInfo = ping6; + }; + 7216D2D90EE89BE900AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2CC0EE89B7900AE70E4; + remoteInfo = rarpd; + }; + 7216D3050EE89D9A00AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2EC0EE89CBC00AE70E4; + remoteInfo = route; + }; + 7216D34C0EE89FEC00AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3130EE89E9E00AE70E4; + remoteInfo = rtadvd; + }; + 7216D37E0EE8A0B300AE70E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3580EE8A02200AE70E4; + remoteInfo = rtsol; + }; + 72179EAD146233390098FB3E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 723C7067142BAFEA007C87E9; + remoteInfo = dnctl; + }; + 72311F4A194A34EB00EB4788 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72311F41194A349000EB4788; + remoteInfo = mptcp_client; + }; + 72311F4C194A34F500EB4788 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72311F41194A349000EB4788; + remoteInfo = mptcp_client; + }; + 72311F4E194A34FE00EB4788 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72311F41194A349000EB4788; + remoteInfo = mptcp_client; + }; + 723C7073142BB003007C87E9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 723C7067142BAFEA007C87E9; + remoteInfo = dnctl; + }; + 724DABC20EE890A6008900D0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 724DABA10EE88FE3008900D0; + remoteInfo = kdumpd; + }; + 724DAC230EE89525008900D0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 724DAC0C0EE8940D008900D0; + remoteInfo = ndp; + }; + 7250E1461616642000A11A76 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7247B83016165EDC00873B3C; + remoteInfo = pktapctl; + }; + 7250E1481616642900A11A76 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7247B83016165EDC00873B3C; + remoteInfo = pktapctl; + }; + 7250E14A1616643000A11A76 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7247B83016165EDC00873B3C; + remoteInfo = pktapctl; + }; + 726121480EE8717B00AFED1B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7261212C0EE8710B00AFED1B; + remoteInfo = arp; + }; + 7261217C0EE8896800AFED1B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 726121530EE8881700AFED1B; + remoteInfo = ifconfig; + }; + 7263724A1BCC709900E4B026 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72946F421BBF055700087E35; + remoteInfo = frame_delay; + }; + 7282BA561AFBDEAD005DE836 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7282BA0B1AFAD4C9005DE836; + remoteInfo = ecnprobe; + }; + 7282BA581AFBDEC2005DE836 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7282BA0B1AFAD4C9005DE836; + remoteInfo = ecnprobe; + }; + 7282BA5A1AFBDED3005DE836 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7282BA0B1AFAD4C9005DE836; + remoteInfo = ecnprobe; + }; + 72946F4B1BBF063800087E35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72946F421BBF055700087E35; + remoteInfo = frame_delay; + }; + 72946F4D1BBF063F00087E35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72946F421BBF055700087E35; + remoteInfo = frame_delay; + }; + 7294F0E90EE8BAC80052EC88 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3A60EE8A3BA00AE70E4; + remoteInfo = spray; + }; + 7294F1200EE8BCC20052EC88 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7294F0F80EE8BB460052EC88; + remoteInfo = traceroute; + }; + 72ABD0871083D750008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 726121530EE8881700AFED1B; + remoteInfo = ifconfig; + }; + 72ABD08B1083D75D008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 724DAC0C0EE8940D008900D0; + remoteInfo = ndp; + }; + 72ABD08D1083D75F008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2450EE896C000AE70E4; + remoteInfo = netstat; + }; + 72ABD08F1083D762008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D27B0EE8980A00AE70E4; + remoteInfo = ping; + }; + 72ABD0911083D764008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2990EE898BD00AE70E4; + remoteInfo = ping6; + }; + 72ABD0931083D767008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2CC0EE89B7900AE70E4; + remoteInfo = rarpd; + }; + 72ABD0951083D76A008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D2EC0EE89CBC00AE70E4; + remoteInfo = route; + }; + 72ABD0971083D76D008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3130EE89E9E00AE70E4; + remoteInfo = rtadvd; + }; + 72ABD0991083D76F008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7216D3580EE8A02200AE70E4; + remoteInfo = rtsol; + }; + 72ABD09B1083D772008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7294F0F80EE8BB460052EC88; + remoteInfo = traceroute; + }; + 72ABD09D1083D774008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7294F1290EE8BD280052EC88; + remoteInfo = traceroute6; + }; + 72ABD0A31083D818008C721C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7261212C0EE8710B00AFED1B; + remoteInfo = arp; + }; + 72B732E81899B18F0060E6D4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72B732D91899B0380060E6D4; + remoteInfo = cfilutil; + }; + 72B732EA1899B19A0060E6D4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72B732D91899B0380060E6D4; + remoteInfo = cfilutil; + }; + 72CD1D9B0EE8C47C005F825D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 724862310EE86EB7001D0DE9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7294F1290EE8BD280052EC88; + remoteInfo = traceroute6; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4D2B05221208CB410004A3F3 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 03EB2F9A120A1DDA0007C1A0 /* ip6addrctl.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 565825AA133921ED003E5FA5 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/share/man/man1; + dstSubfolderSpec = 0; + files = ( + 565825A9133921CF003E5FA5 /* mnc.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 690D97AD12DE7074004323A7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 690D97AE12DE70AE004323A7 /* mtest.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 69C10A6312DF7D5300BCDF4C /* Install OSS Plist */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/OpenSourceVersions/; + dstSubfolderSpec = 0; + files = ( + 72E42BA314B7CF3D003AAE28 /* network_cmds.plist in Install OSS Plist */, + ); + name = "Install OSS Plist"; + runOnlyForDeploymentPostprocessing = 1; + }; + 713297661A93C743002359CF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man5; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 71D958BB1A9452C200C9B286 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 71D958BE1A9453A500C9B286 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/etc/unbound; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7200F2F81958A34D0033E22C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D2750EE8979500AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + 7216D2600EE8971500AE70E4 /* netstat.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D2930EE8988200AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D2880EE8982F00AE70E4 /* ping.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D2AA0EE8991200AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D2A90EE898F300AE70E4 /* ping6.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D2E20EE89BEA00AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D2E40EE89C8B00AE70E4 /* rarpd.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D2FA0EE89D2600AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D3040EE89D4900AE70E4 /* route.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D3370EE89F7500AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D3240EE89F4200AE70E4 /* rtadvd.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D3380EE89F7500AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man5; + dstSubfolderSpec = 0; + files = ( + 7216D3390EE89F8400AE70E4 /* rtadvd.conf.5 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D3460EE89FD700AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /private/etc; + dstSubfolderSpec = 0; + files = ( + 7216D3410EE89FAF00AE70E4 /* rtadvd.conf in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D37B0EE8A06200AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D3700EE8A05B00AE70E4 /* rtsol.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7216D3C10EE8A42D00AE70E4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7216D3AF0EE8A3D800AE70E4 /* spray.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 72311F40194A349000EB4788 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 72311F56194A76DA00EB4788 /* mptcp_client.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 723C7066142BAFEA007C87E9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 724753E7144905E300F6A941 /* dnctl.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7247B82F16165EDC00873B3C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/local/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + 7247B83616165EDC00873B3C /* pktapctl.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 724DABB70EE89035008900D0 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 724DABAB0EE89006008900D0 /* kdumpd.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 724DABC10EE89090008900D0 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/LaunchDaemons; + dstSubfolderSpec = 0; + files = ( + 724DABBC0EE8908A008900D0 /* com.apple.kdumpd.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 724DAC1D0EE894E8008900D0 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 724DAC3B0EE89555008900D0 /* ndp.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7261213F0EE8713B00AFED1B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 726121350EE8713800AFED1B /* arp.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 726121620EE8887300AFED1B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 726121610EE8885400AFED1B /* ifconfig.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7282BA0A1AFAD4C9005DE836 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 72946F411BBF055700087E35 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7294F11A0EE8BC0C0052EC88 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7294F1080EE8BBB10052EC88 /* traceroute.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 7294F13E0EE8BDB30052EC88 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 7294F1320EE8BD430052EC88 /* traceroute6.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 72B732D81899B0380060E6D4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 72B732DF1899B0380060E6D4 /* cfilutil.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 01560DFC241969D3001AA29A /* vsock.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vsock.c; sourceTree = "<group>"; }; + 4D2B04E41208C12F0004A3F3 /* ip6addrctl.8 */ = {isa = PBXFileReference; explicitFileType = text; fileEncoding = 4; path = ip6addrctl.8; sourceTree = "<group>"; }; + 4D2B04E51208C12F0004A3F3 /* ip6addrctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ip6addrctl.c; sourceTree = "<group>"; }; + 4D2B04E61208C12F0004A3F3 /* ip6addrctl.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ip6addrctl.conf; sourceTree = "<group>"; }; + 4D2B04F31208C2040004A3F3 /* ip6addrctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ip6addrctl; sourceTree = BUILT_PRODUCTS_DIR; }; + 565825941339217B003E5FA5 /* mnc.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = mnc.1; sourceTree = "<group>"; }; + 565825951339217B003E5FA5 /* LICENCE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENCE; sourceTree = "<group>"; }; + 565825961339217B003E5FA5 /* mnc_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mnc_error.c; sourceTree = "<group>"; }; + 565825971339217B003E5FA5 /* mnc_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mnc_main.c; sourceTree = "<group>"; }; + 565825981339217B003E5FA5 /* mnc_multicast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mnc_multicast.c; sourceTree = "<group>"; }; + 565825991339217B003E5FA5 /* mnc_opts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mnc_opts.c; sourceTree = "<group>"; }; + 5658259A1339217B003E5FA5 /* mnc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mnc.h; sourceTree = "<group>"; }; + 5658259B1339217B003E5FA5 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; }; + 5658259F1339218F003E5FA5 /* mnc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mnc; sourceTree = BUILT_PRODUCTS_DIR; }; + 56B6B66716F79A1C00D8A7A9 /* mptcp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mptcp.c; sourceTree = "<group>"; }; + 690D978112DE6034004323A7 /* mtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mtest; sourceTree = BUILT_PRODUCTS_DIR; }; + 690D979412DE6E6B004323A7 /* mtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mtest.c; sourceTree = "<group>"; }; + 690D979512DE6E76004323A7 /* mtest.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mtest.8; sourceTree = "<group>"; }; + 69C10A7912DF80F200BCDF4C /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; }; + 713297681A93C743002359CF /* unbound */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unbound; sourceTree = BUILT_PRODUCTS_DIR; }; + 7200F2FA1958A34D0033E22C /* pktmnglr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pktmnglr; sourceTree = BUILT_PRODUCTS_DIR; }; + 7200F2FC1958A34D0033E22C /* packet_mangler.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = packet_mangler.c; sourceTree = "<group>"; }; + 7211D9B2190713A60086EF20 /* network-client-server-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "network-client-server-entitlements.plist"; sourceTree = "<group>"; }; + 721654C21EC52447005B17BA /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = "<group>"; }; + 7216D2460EE896C000AE70E4 /* netstat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = netstat; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D27C0EE8980A00AE70E4 /* ping */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ping; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D29A0EE898BD00AE70E4 /* ping6 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ping6; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D2CD0EE89B7900AE70E4 /* rarpd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rarpd; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D2ED0EE89CBC00AE70E4 /* route */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = route; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D3140EE89E9E00AE70E4 /* rtadvd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rtadvd; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D3590EE8A02200AE70E4 /* rtsol */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rtsol; sourceTree = BUILT_PRODUCTS_DIR; }; + 7216D3A70EE8A3BB00AE70E4 /* spray */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = spray; sourceTree = BUILT_PRODUCTS_DIR; }; + 7218B549191D4202001B7B52 /* systm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = systm.c; sourceTree = "<group>"; }; + 72311F42194A349000EB4788 /* mptcp_client */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mptcp_client; sourceTree = BUILT_PRODUCTS_DIR; }; + 72311F50194A354F00EB4788 /* conn_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = conn_lib.c; sourceTree = "<group>"; }; + 72311F51194A354F00EB4788 /* conn_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = conn_lib.h; sourceTree = "<group>"; }; + 72311F52194A354F00EB4788 /* mptcp_client.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = mptcp_client.1; sourceTree = "<group>"; }; + 72311F53194A354F00EB4788 /* mptcp_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mptcp_client.c; sourceTree = "<group>"; }; + 723C7068142BAFEA007C87E9 /* dnctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnctl; sourceTree = BUILT_PRODUCTS_DIR; }; + 724753E61448E1EF00F6A941 /* dnctl.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = dnctl.8; sourceTree = "<group>"; }; + 7247B83116165EDC00873B3C /* pktapctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pktapctl; sourceTree = BUILT_PRODUCTS_DIR; }; + 7247B83516165EDC00873B3C /* pktapctl.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = pktapctl.8; sourceTree = "<group>"; }; + 7247B83B16165F0100873B3C /* pktapctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pktapctl.c; sourceTree = "<group>"; }; + 724DABA20EE88FE3008900D0 /* kdumpd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = kdumpd; sourceTree = BUILT_PRODUCTS_DIR; }; + 724DAC0D0EE8940D008900D0 /* ndp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ndp; sourceTree = BUILT_PRODUCTS_DIR; }; + 7255D4301E44036D008F4A32 /* libcrypto.35.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.35.dylib; path = usr/lib/libcrypto.35.dylib; sourceTree = SDKROOT; }; + 7255D4321E44037F008F4A32 /* libssl.35.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.35.dylib; path = usr/lib/libssl.35.dylib; sourceTree = SDKROOT; }; + 7261204D0EE86EF900AFED1B /* arp.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = arp.8; sourceTree = "<group>"; }; + 7261204E0EE86EF900AFED1B /* arp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = arp.c; sourceTree = "<group>"; }; + 7261204F0EE86EF900AFED1B /* arp4.4 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = arp4.4; sourceTree = "<group>"; }; + 726120550EE86F0900AFED1B /* ifbond.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifbond.c; sourceTree = "<group>"; }; + 726120560EE86F0900AFED1B /* ifconfig.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ifconfig.8; sourceTree = "<group>"; }; + 726120570EE86F0900AFED1B /* ifconfig.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifconfig.c; sourceTree = "<group>"; }; + 726120580EE86F0900AFED1B /* ifconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ifconfig.h; sourceTree = "<group>"; }; + 726120590EE86F0900AFED1B /* ifmedia.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifmedia.c; sourceTree = "<group>"; }; + 7261205A0EE86F0900AFED1B /* ifvlan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifvlan.c; sourceTree = "<group>"; }; + 7261206E0EE86F2D00AFED1B /* com.apple.kdumpd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.kdumpd.plist; sourceTree = "<group>"; }; + 7261206F0EE86F2D00AFED1B /* kdump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kdump.h; sourceTree = "<group>"; }; + 726120700EE86F2D00AFED1B /* kdumpd.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = kdumpd.8; sourceTree = "<group>"; }; + 726120710EE86F2D00AFED1B /* kdumpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kdumpd.c; sourceTree = "<group>"; }; + 726120720EE86F2D00AFED1B /* kdumpsubs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kdumpsubs.c; sourceTree = "<group>"; }; + 726120730EE86F2D00AFED1B /* kdumpsubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kdumpsubs.h; sourceTree = "<group>"; }; + 726120840EE86F4000AFED1B /* gnuc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gnuc.h; sourceTree = "<group>"; }; + 726120860EE86F4000AFED1B /* ndp.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ndp.8; sourceTree = "<group>"; }; + 726120870EE86F4000AFED1B /* ndp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ndp.c; sourceTree = "<group>"; }; + 7261208B0EE86F4800AFED1B /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; }; + 7261208C0EE86F4800AFED1B /* DERIVED_FILES */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DERIVED_FILES; sourceTree = "<group>"; }; + 7261208D0EE86F4800AFED1B /* if.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = if.c; sourceTree = "<group>"; }; + 7261208E0EE86F4800AFED1B /* inet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inet.c; sourceTree = "<group>"; }; + 7261208F0EE86F4800AFED1B /* inet6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inet6.c; sourceTree = "<group>"; }; + 726120900EE86F4800AFED1B /* ipsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ipsec.c; sourceTree = "<group>"; }; + 726120910EE86F4800AFED1B /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; }; + 726120930EE86F4800AFED1B /* mbuf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mbuf.c; sourceTree = "<group>"; }; + 726120940EE86F4800AFED1B /* mcast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mcast.c; sourceTree = "<group>"; }; + 726120970EE86F4800AFED1B /* netstat.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = netstat.1; sourceTree = "<group>"; }; + 726120980EE86F4800AFED1B /* netstat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = netstat.h; sourceTree = "<group>"; }; + 726120990EE86F4800AFED1B /* route.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = route.c; sourceTree = "<group>"; }; + 7261209A0EE86F4800AFED1B /* tp_astring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tp_astring.c; sourceTree = "<group>"; }; + 7261209B0EE86F4800AFED1B /* unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unix.c; sourceTree = "<group>"; }; + 726120A00EE86F5000AFED1B /* ping.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ping.8; sourceTree = "<group>"; }; + 726120A10EE86F5000AFED1B /* ping.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ping.c; sourceTree = "<group>"; }; + 726120A60EE86F5C00AFED1B /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = md5.c; sourceTree = "<group>"; }; + 726120A70EE86F5C00AFED1B /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; }; + 726120A80EE86F5C00AFED1B /* ping6.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ping6.8; sourceTree = "<group>"; }; + 726120A90EE86F5C00AFED1B /* ping6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ping6.c; sourceTree = "<group>"; }; + 726120AE0EE86F6700AFED1B /* rarpd.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = rarpd.8; sourceTree = "<group>"; }; + 726120AF0EE86F6700AFED1B /* rarpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rarpd.c; sourceTree = "<group>"; }; + 726120B30EE86F7200AFED1B /* gen_header.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = gen_header.pl; sourceTree = "<group>"; }; + 726120B40EE86F7200AFED1B /* keywords */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = keywords; sourceTree = "<group>"; }; + 726120B50EE86F7200AFED1B /* keywords.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keywords.h; sourceTree = "<group>"; }; + 726120B70EE86F7200AFED1B /* route.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = route.8; sourceTree = "<group>"; }; + 726120B80EE86F7200AFED1B /* route.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = route.c; sourceTree = "<group>"; }; + 726120BC0EE86F8200AFED1B /* advcap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = advcap.c; sourceTree = "<group>"; }; + 726120BD0EE86F8200AFED1B /* advcap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = advcap.h; sourceTree = "<group>"; }; + 726120BE0EE86F8200AFED1B /* config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = config.c; sourceTree = "<group>"; }; + 726120BF0EE86F8200AFED1B /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; }; + 726120C00EE86F8200AFED1B /* dump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dump.c; sourceTree = "<group>"; }; + 726120C10EE86F8200AFED1B /* dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dump.h; sourceTree = "<group>"; }; + 726120C20EE86F8200AFED1B /* if.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = if.c; sourceTree = "<group>"; }; + 726120C30EE86F8200AFED1B /* if.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = if.h; sourceTree = "<group>"; }; + 726120C50EE86F8200AFED1B /* pathnames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; }; + 726120C60EE86F8200AFED1B /* rrenum.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rrenum.c; sourceTree = "<group>"; }; + 726120C70EE86F8200AFED1B /* rrenum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rrenum.h; sourceTree = "<group>"; }; + 726120C80EE86F8200AFED1B /* rtadvd.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = rtadvd.8; sourceTree = "<group>"; }; + 726120C90EE86F8200AFED1B /* rtadvd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtadvd.c; sourceTree = "<group>"; }; + 726120CA0EE86F8200AFED1B /* rtadvd.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = rtadvd.conf; sourceTree = "<group>"; }; + 726120CB0EE86F8200AFED1B /* rtadvd.conf.5 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = rtadvd.conf.5; sourceTree = "<group>"; }; + 726120CC0EE86F8200AFED1B /* rtadvd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtadvd.h; sourceTree = "<group>"; }; + 726120CD0EE86F8200AFED1B /* timer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = timer.c; sourceTree = "<group>"; }; + 726120CE0EE86F8200AFED1B /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = "<group>"; }; + 726120D20EE86F9100AFED1B /* dump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dump.c; sourceTree = "<group>"; }; + 726120D30EE86F9100AFED1B /* if.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = if.c; sourceTree = "<group>"; }; + 726120D50EE86F9100AFED1B /* probe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = probe.c; sourceTree = "<group>"; }; + 726120D60EE86F9100AFED1B /* rtsock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtsock.c; sourceTree = "<group>"; }; + 726120D70EE86F9100AFED1B /* rtsol.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = rtsol.8; sourceTree = "<group>"; }; + 726120D80EE86F9100AFED1B /* rtsol.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtsol.c; sourceTree = "<group>"; }; + 726120D90EE86F9100AFED1B /* rtsold.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtsold.c; sourceTree = "<group>"; }; + 726120DA0EE86F9100AFED1B /* rtsold.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtsold.h; sourceTree = "<group>"; }; + 726120DF0EE86F9D00AFED1B /* spray.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = spray.8; sourceTree = "<group>"; }; + 726120E00EE86F9D00AFED1B /* spray.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = spray.c; sourceTree = "<group>"; }; + 726120E10EE86F9D00AFED1B /* spray.x */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = spray.x; sourceTree = "<group>"; }; + 726120E50EE86FA700AFED1B /* as.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = as.c; sourceTree = "<group>"; }; + 726120E60EE86FA700AFED1B /* as.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = as.h; sourceTree = "<group>"; }; + 726120E70EE86FA700AFED1B /* findsaddr-socket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "findsaddr-socket.c"; sourceTree = "<group>"; }; + 726120E80EE86FA700AFED1B /* findsaddr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = findsaddr.h; sourceTree = "<group>"; }; + 726120E90EE86FA700AFED1B /* gnuc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gnuc.h; sourceTree = "<group>"; }; + 726120EA0EE86FA700AFED1B /* ifaddrlist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifaddrlist.c; sourceTree = "<group>"; }; + 726120EB0EE86FA700AFED1B /* ifaddrlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ifaddrlist.h; sourceTree = "<group>"; }; + 726120ED0EE86FA700AFED1B /* mean.awk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mean.awk; sourceTree = "<group>"; }; + 726120EE0EE86FA700AFED1B /* median.awk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = median.awk; sourceTree = "<group>"; }; + 726120EF0EE86FA700AFED1B /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; }; + 726120F00EE86FA700AFED1B /* traceroute.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = traceroute.8; sourceTree = "<group>"; }; + 726120F10EE86FA700AFED1B /* traceroute.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = traceroute.c; sourceTree = "<group>"; }; + 726120F20EE86FA700AFED1B /* traceroute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = traceroute.h; sourceTree = "<group>"; }; + 726120F30EE86FA700AFED1B /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = "<group>"; }; + 726120FA0EE86FB500AFED1B /* traceroute6.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = traceroute6.8; sourceTree = "<group>"; }; + 726120FB0EE86FB500AFED1B /* traceroute6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = traceroute6.c; sourceTree = "<group>"; }; + 7261212D0EE8710B00AFED1B /* arp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = arp; sourceTree = BUILT_PRODUCTS_DIR; }; + 726121540EE8881700AFED1B /* ifconfig */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ifconfig; sourceTree = BUILT_PRODUCTS_DIR; }; + 7263724C1BCC718B00E4B026 /* frame_delay.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = frame_delay.8; sourceTree = "<group>"; }; + 7282BA0C1AFAD4C9005DE836 /* ecnprobe */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ecnprobe; sourceTree = BUILT_PRODUCTS_DIR; }; + 7282BA341AFAD58E005DE836 /* base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base.h; sourceTree = "<group>"; }; + 7282BA351AFAD58E005DE836 /* capture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = capture.c; sourceTree = "<group>"; }; + 7282BA361AFAD58E005DE836 /* capture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = capture.h; sourceTree = "<group>"; }; + 7282BA391AFAD58E005DE836 /* ecn_probe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ecn_probe.c; sourceTree = "<group>"; }; + 7282BA3A1AFAD58E005DE836 /* ecn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ecn.c; sourceTree = "<group>"; }; + 7282BA3B1AFAD58E005DE836 /* ecn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ecn.h; sourceTree = "<group>"; }; + 7282BA3C1AFAD58E005DE836 /* gmt2local.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gmt2local.c; sourceTree = "<group>"; }; + 7282BA3D1AFAD58E005DE836 /* gmt2local.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gmt2local.h; sourceTree = "<group>"; }; + 7282BA3E1AFAD58E005DE836 /* history.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = history.c; sourceTree = "<group>"; }; + 7282BA3F1AFAD58E005DE836 /* history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = history.h; sourceTree = "<group>"; }; + 7282BA401AFAD58E005DE836 /* inet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inet.c; sourceTree = "<group>"; }; + 7282BA411AFAD58E005DE836 /* inet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inet.h; sourceTree = "<group>"; }; + 7282BA451AFAD58E005DE836 /* session.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = session.c; sourceTree = "<group>"; }; + 7282BA461AFAD58E005DE836 /* session.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = session.h; sourceTree = "<group>"; }; + 7282BA471AFAD58E005DE836 /* support.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = support.c; sourceTree = "<group>"; }; + 7282BA481AFAD58E005DE836 /* support.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = support.h; sourceTree = "<group>"; }; + 7282BA541AFBCA66005DE836 /* libpcap.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpcap.dylib; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libpcap.dylib; sourceTree = DEVELOPER_DIR; }; + 7282BA5C1AFBED07005DE836 /* ecnprobe.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = ecnprobe.1; sourceTree = "<group>"; }; + 72946F431BBF055700087E35 /* frame_delay */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = frame_delay; sourceTree = BUILT_PRODUCTS_DIR; }; + 72946F4F1BBF07FF00087E35 /* frame_delay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = frame_delay.c; sourceTree = "<group>"; }; + 7294F0F90EE8BB460052EC88 /* traceroute */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = traceroute; sourceTree = BUILT_PRODUCTS_DIR; }; + 7294F12A0EE8BD280052EC88 /* traceroute6 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = traceroute6; sourceTree = BUILT_PRODUCTS_DIR; }; + 72B732DA1899B0380060E6D4 /* cfilutil */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cfilutil; sourceTree = BUILT_PRODUCTS_DIR; }; + 72B732DE1899B0380060E6D4 /* cfilutil.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cfilutil.1; sourceTree = "<group>"; }; + 72B732EE1899B23A0060E6D4 /* cfilutil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cfilutil.c; sourceTree = "<group>"; }; + 72B732F01899B2430060E6D4 /* cfilstat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cfilstat.c; sourceTree = "<group>"; }; + 72B7F36A1BA69281003A9AA2 /* if6lowpan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = if6lowpan.c; sourceTree = "<group>"; }; + 72CD1DB50EE8C619005F825D /* libipsec.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libipsec.dylib; path = /usr/lib/libipsec.dylib; sourceTree = "<absolute>"; }; + 72D000C3142BB11100151981 /* dnctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dnctl.c; sourceTree = "<group>"; }; + 72D33F552271220100EF5B5E /* rtadvd_logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtadvd_logging.h; sourceTree = "<group>"; }; + 72D33F562271220100EF5B5E /* rtadvd_logging.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtadvd_logging.c; sourceTree = "<group>"; }; + 72E42BA214B7CF37003AAE28 /* network_cmds.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = network_cmds.plist; sourceTree = "<group>"; }; + 72E650A2107BF2F000AAF325 /* af_inet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = af_inet.c; sourceTree = "<group>"; }; + 72E650A3107BF2F000AAF325 /* af_inet6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = af_inet6.c; sourceTree = "<group>"; }; + 72E650A4107BF2F000AAF325 /* af_link.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = af_link.c; sourceTree = "<group>"; }; + 72E650A5107BF2F000AAF325 /* ifbridge.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifbridge.c; sourceTree = "<group>"; }; + 72E650A6107BF2F000AAF325 /* ifclone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifclone.c; sourceTree = "<group>"; }; + E01AB08F1368880F008C66FF /* libutil.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libutil.dylib; path = $SDKROOT/usr/lib/libutil.dylib; sourceTree = "<group>"; }; + F940359C1E2FF58500283EB1 /* iffake.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iffake.c; sourceTree = "<group>"; }; + F97F1E031E9C3FBC002355FF /* nexus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nexus.c; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5658259D1339218F003E5FA5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 690D977F12DE6034004323A7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 713297651A93C743002359CF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7200F2F71958A34D0033E22C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2440EE896C000AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D27A0EE8980A00AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2980EE898BD00AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7263A9630EEE31C800164D5D /* libipsec.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2CB0EE89B7900AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2EB0EE89CBC00AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D3120EE89E9E00AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E01AB0901368880F008C66FF /* libutil.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D3570EE8A02200AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D3A50EE8A3BA00AE70E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72311F3F194A349000EB4788 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 723C7065142BAFEA007C87E9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7247B82E16165EDC00873B3C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 724DABA00EE88FE3008900D0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 724DAC0B0EE8940D008900D0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7261212B0EE8710B00AFED1B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 726121520EE8881700AFED1B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7282BA091AFAD4C9005DE836 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7282BA551AFBCA66005DE836 /* libpcap.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72946F401BBF055700087E35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7294F0F70EE8BB460052EC88 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7294F1280EE8BD280052EC88 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 72B894EC0EEDB17C00C218D6 /* libipsec.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72B732D71899B0380060E6D4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4D2B04E31208C12F0004A3F3 /* ip6addrctl.tproj */ = { + isa = PBXGroup; + children = ( + 4D2B04E41208C12F0004A3F3 /* ip6addrctl.8 */, + 4D2B04E51208C12F0004A3F3 /* ip6addrctl.c */, + 4D2B04E61208C12F0004A3F3 /* ip6addrctl.conf */, + ); + path = ip6addrctl.tproj; + sourceTree = "<group>"; + }; + 56582591133920B5003E5FA5 /* mnc.tproj */ = { + isa = PBXGroup; + children = ( + 565825941339217B003E5FA5 /* mnc.1 */, + 565825951339217B003E5FA5 /* LICENCE */, + 565825961339217B003E5FA5 /* mnc_error.c */, + 565825971339217B003E5FA5 /* mnc_main.c */, + 565825981339217B003E5FA5 /* mnc_multicast.c */, + 565825991339217B003E5FA5 /* mnc_opts.c */, + 5658259A1339217B003E5FA5 /* mnc.h */, + 5658259B1339217B003E5FA5 /* README */, + ); + path = mnc.tproj; + sourceTree = "<group>"; + }; + 690D973F12DE5A21004323A7 /* mtest.tproj */ = { + isa = PBXGroup; + children = ( + 690D979512DE6E76004323A7 /* mtest.8 */, + 690D979412DE6E6B004323A7 /* mtest.c */, + 69C10A7912DF80F200BCDF4C /* COPYING */, + ); + path = mtest.tproj; + sourceTree = "<group>"; + }; + 7200F2FB1958A34D0033E22C /* pktmnglr */ = { + isa = PBXGroup; + children = ( + 7200F2FC1958A34D0033E22C /* packet_mangler.c */, + ); + path = pktmnglr; + sourceTree = "<group>"; + }; + 72311F43194A349100EB4788 /* mptcp_client */ = { + isa = PBXGroup; + children = ( + 72311F53194A354F00EB4788 /* mptcp_client.c */, + 72311F50194A354F00EB4788 /* conn_lib.c */, + 72311F51194A354F00EB4788 /* conn_lib.h */, + 72311F52194A354F00EB4788 /* mptcp_client.1 */, + ); + path = mptcp_client; + sourceTree = "<group>"; + }; + 723C706A142BAFEA007C87E9 /* dnctl */ = { + isa = PBXGroup; + children = ( + 724753E61448E1EF00F6A941 /* dnctl.8 */, + 72D000C3142BB11100151981 /* dnctl.c */, + ); + path = dnctl; + sourceTree = "<group>"; + }; + 7247B83216165EDC00873B3C /* pktapctl */ = { + isa = PBXGroup; + children = ( + 7247B83B16165F0100873B3C /* pktapctl.c */, + 7247B83516165EDC00873B3C /* pktapctl.8 */, + ); + path = pktapctl; + sourceTree = "<group>"; + }; + 7248622F0EE86EB7001D0DE9 = { + isa = PBXGroup; + children = ( + 7282BA541AFBCA66005DE836 /* libpcap.dylib */, + 72E42BA214B7CF37003AAE28 /* network_cmds.plist */, + 7211D9B2190713A60086EF20 /* network-client-server-entitlements.plist */, + E01AB08F1368880F008C66FF /* libutil.dylib */, + 7261204C0EE86EF900AFED1B /* arp.tproj */, + 72B732DB1899B0380060E6D4 /* cfilutil */, + 723C706A142BAFEA007C87E9 /* dnctl */, + 7282BA0D1AFAD4C9005DE836 /* ecnprobe */, + 72946F441BBF055700087E35 /* frame_delay */, + 726120540EE86F0900AFED1B /* ifconfig.tproj */, + 4D2B04E31208C12F0004A3F3 /* ip6addrctl.tproj */, + 7261206D0EE86F2D00AFED1B /* kdumpd.tproj */, + 56582591133920B5003E5FA5 /* mnc.tproj */, + 72311F43194A349100EB4788 /* mptcp_client */, + 690D973F12DE5A21004323A7 /* mtest.tproj */, + 726120830EE86F4000AFED1B /* ndp.tproj */, + 7261208A0EE86F4800AFED1B /* netstat.tproj */, + 7247B83216165EDC00873B3C /* pktapctl */, + 7200F2FB1958A34D0033E22C /* pktmnglr */, + 7261209E0EE86F5000AFED1B /* ping.tproj */, + 726120A40EE86F5C00AFED1B /* ping6.tproj */, + 726120AC0EE86F6700AFED1B /* rarpd.tproj */, + 726120B20EE86F7200AFED1B /* route.tproj */, + 726120BB0EE86F8200AFED1B /* rtadvd.tproj */, + 726120D10EE86F9100AFED1B /* rtsol.tproj */, + 726120DD0EE86F9D00AFED1B /* spray.tproj */, + 726120E40EE86FA700AFED1B /* traceroute.tproj */, + 726120F80EE86FB500AFED1B /* traceroute6.tproj */, + 72CD1DB50EE8C619005F825D /* libipsec.dylib */, + 7261210D0EE8707500AFED1B /* Products */, + 7255D42F1E44036D008F4A32 /* Frameworks */, + ); + sourceTree = "<group>"; + }; + 7255D42F1E44036D008F4A32 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7255D4321E44037F008F4A32 /* libssl.35.dylib */, + 7255D4301E44036D008F4A32 /* libcrypto.35.dylib */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + 7261204C0EE86EF900AFED1B /* arp.tproj */ = { + isa = PBXGroup; + children = ( + 7261204D0EE86EF900AFED1B /* arp.8 */, + 7261204E0EE86EF900AFED1B /* arp.c */, + 7261204F0EE86EF900AFED1B /* arp4.4 */, + ); + path = arp.tproj; + sourceTree = "<group>"; + }; + 726120540EE86F0900AFED1B /* ifconfig.tproj */ = { + isa = PBXGroup; + children = ( + 72B7F36A1BA69281003A9AA2 /* if6lowpan.c */, + 72E650A2107BF2F000AAF325 /* af_inet.c */, + 72E650A3107BF2F000AAF325 /* af_inet6.c */, + 72E650A4107BF2F000AAF325 /* af_link.c */, + 72E650A5107BF2F000AAF325 /* ifbridge.c */, + 72E650A6107BF2F000AAF325 /* ifclone.c */, + 726120550EE86F0900AFED1B /* ifbond.c */, + 726120560EE86F0900AFED1B /* ifconfig.8 */, + 726120570EE86F0900AFED1B /* ifconfig.c */, + 726120580EE86F0900AFED1B /* ifconfig.h */, + F940359C1E2FF58500283EB1 /* iffake.c */, + 726120590EE86F0900AFED1B /* ifmedia.c */, + 7261205A0EE86F0900AFED1B /* ifvlan.c */, + F97F1E031E9C3FBC002355FF /* nexus.c */, + ); + path = ifconfig.tproj; + sourceTree = "<group>"; + }; + 7261206D0EE86F2D00AFED1B /* kdumpd.tproj */ = { + isa = PBXGroup; + children = ( + 7261206E0EE86F2D00AFED1B /* com.apple.kdumpd.plist */, + 7261206F0EE86F2D00AFED1B /* kdump.h */, + 726120700EE86F2D00AFED1B /* kdumpd.8 */, + 726120710EE86F2D00AFED1B /* kdumpd.c */, + 726120720EE86F2D00AFED1B /* kdumpsubs.c */, + 726120730EE86F2D00AFED1B /* kdumpsubs.h */, + ); + path = kdumpd.tproj; + sourceTree = "<group>"; + }; + 726120830EE86F4000AFED1B /* ndp.tproj */ = { + isa = PBXGroup; + children = ( + 726120840EE86F4000AFED1B /* gnuc.h */, + 726120860EE86F4000AFED1B /* ndp.8 */, + 726120870EE86F4000AFED1B /* ndp.c */, + ); + path = ndp.tproj; + sourceTree = "<group>"; + }; + 7261208A0EE86F4800AFED1B /* netstat.tproj */ = { + isa = PBXGroup; + children = ( + 7261208B0EE86F4800AFED1B /* data.c */, + 7261208C0EE86F4800AFED1B /* DERIVED_FILES */, + 7261208D0EE86F4800AFED1B /* if.c */, + 7261208E0EE86F4800AFED1B /* inet.c */, + 7261208F0EE86F4800AFED1B /* inet6.c */, + 726120900EE86F4800AFED1B /* ipsec.c */, + 726120910EE86F4800AFED1B /* main.c */, + 726120930EE86F4800AFED1B /* mbuf.c */, + 726120940EE86F4800AFED1B /* mcast.c */, + 56B6B66716F79A1C00D8A7A9 /* mptcp.c */, + 726120970EE86F4800AFED1B /* netstat.1 */, + 726120980EE86F4800AFED1B /* netstat.h */, + 726120990EE86F4800AFED1B /* route.c */, + 7261209A0EE86F4800AFED1B /* tp_astring.c */, + 7261209B0EE86F4800AFED1B /* unix.c */, + 7218B549191D4202001B7B52 /* systm.c */, + 721654C21EC52447005B17BA /* misc.c */, + 01560DFC241969D3001AA29A /* vsock.c */, + ); + path = netstat.tproj; + sourceTree = "<group>"; + }; + 7261209E0EE86F5000AFED1B /* ping.tproj */ = { + isa = PBXGroup; + children = ( + 726120A00EE86F5000AFED1B /* ping.8 */, + 726120A10EE86F5000AFED1B /* ping.c */, + ); + path = ping.tproj; + sourceTree = "<group>"; + }; + 726120A40EE86F5C00AFED1B /* ping6.tproj */ = { + isa = PBXGroup; + children = ( + 726120A60EE86F5C00AFED1B /* md5.c */, + 726120A70EE86F5C00AFED1B /* md5.h */, + 726120A80EE86F5C00AFED1B /* ping6.8 */, + 726120A90EE86F5C00AFED1B /* ping6.c */, + ); + path = ping6.tproj; + sourceTree = "<group>"; + }; + 726120AC0EE86F6700AFED1B /* rarpd.tproj */ = { + isa = PBXGroup; + children = ( + 726120AE0EE86F6700AFED1B /* rarpd.8 */, + 726120AF0EE86F6700AFED1B /* rarpd.c */, + ); + path = rarpd.tproj; + sourceTree = "<group>"; + }; + 726120B20EE86F7200AFED1B /* route.tproj */ = { + isa = PBXGroup; + children = ( + 726120B30EE86F7200AFED1B /* gen_header.pl */, + 726120B40EE86F7200AFED1B /* keywords */, + 726120B50EE86F7200AFED1B /* keywords.h */, + 726120B70EE86F7200AFED1B /* route.8 */, + 726120B80EE86F7200AFED1B /* route.c */, + ); + path = route.tproj; + sourceTree = "<group>"; + }; + 726120BB0EE86F8200AFED1B /* rtadvd.tproj */ = { + isa = PBXGroup; + children = ( + 72D33F562271220100EF5B5E /* rtadvd_logging.c */, + 72D33F552271220100EF5B5E /* rtadvd_logging.h */, + 726120BC0EE86F8200AFED1B /* advcap.c */, + 726120BD0EE86F8200AFED1B /* advcap.h */, + 726120BE0EE86F8200AFED1B /* config.c */, + 726120BF0EE86F8200AFED1B /* config.h */, + 726120C00EE86F8200AFED1B /* dump.c */, + 726120C10EE86F8200AFED1B /* dump.h */, + 726120C20EE86F8200AFED1B /* if.c */, + 726120C30EE86F8200AFED1B /* if.h */, + 726120C50EE86F8200AFED1B /* pathnames.h */, + 726120C60EE86F8200AFED1B /* rrenum.c */, + 726120C70EE86F8200AFED1B /* rrenum.h */, + 726120C80EE86F8200AFED1B /* rtadvd.8 */, + 726120C90EE86F8200AFED1B /* rtadvd.c */, + 726120CA0EE86F8200AFED1B /* rtadvd.conf */, + 726120CB0EE86F8200AFED1B /* rtadvd.conf.5 */, + 726120CC0EE86F8200AFED1B /* rtadvd.h */, + 726120CD0EE86F8200AFED1B /* timer.c */, + 726120CE0EE86F8200AFED1B /* timer.h */, + ); + path = rtadvd.tproj; + sourceTree = "<group>"; + }; + 726120D10EE86F9100AFED1B /* rtsol.tproj */ = { + isa = PBXGroup; + children = ( + 726120D20EE86F9100AFED1B /* dump.c */, + 726120D30EE86F9100AFED1B /* if.c */, + 726120D50EE86F9100AFED1B /* probe.c */, + 726120D60EE86F9100AFED1B /* rtsock.c */, + 726120D70EE86F9100AFED1B /* rtsol.8 */, + 726120D80EE86F9100AFED1B /* rtsol.c */, + 726120D90EE86F9100AFED1B /* rtsold.c */, + 726120DA0EE86F9100AFED1B /* rtsold.h */, + ); + path = rtsol.tproj; + sourceTree = "<group>"; + }; + 726120DD0EE86F9D00AFED1B /* spray.tproj */ = { + isa = PBXGroup; + children = ( + 726120DF0EE86F9D00AFED1B /* spray.8 */, + 726120E00EE86F9D00AFED1B /* spray.c */, + 726120E10EE86F9D00AFED1B /* spray.x */, + ); + path = spray.tproj; + sourceTree = "<group>"; + }; + 726120E40EE86FA700AFED1B /* traceroute.tproj */ = { + isa = PBXGroup; + children = ( + 726120E50EE86FA700AFED1B /* as.c */, + 726120E60EE86FA700AFED1B /* as.h */, + 726120E70EE86FA700AFED1B /* findsaddr-socket.c */, + 726120E80EE86FA700AFED1B /* findsaddr.h */, + 726120E90EE86FA700AFED1B /* gnuc.h */, + 726120EA0EE86FA700AFED1B /* ifaddrlist.c */, + 726120EB0EE86FA700AFED1B /* ifaddrlist.h */, + 726120ED0EE86FA700AFED1B /* mean.awk */, + 726120EE0EE86FA700AFED1B /* median.awk */, + 726120EF0EE86FA700AFED1B /* README */, + 726120F00EE86FA700AFED1B /* traceroute.8 */, + 726120F10EE86FA700AFED1B /* traceroute.c */, + 726120F20EE86FA700AFED1B /* traceroute.h */, + 726120F30EE86FA700AFED1B /* version.c */, + ); + path = traceroute.tproj; + sourceTree = "<group>"; + }; + 726120F80EE86FB500AFED1B /* traceroute6.tproj */ = { + isa = PBXGroup; + children = ( + 726120FA0EE86FB500AFED1B /* traceroute6.8 */, + 726120FB0EE86FB500AFED1B /* traceroute6.c */, + ); + path = traceroute6.tproj; + sourceTree = "<group>"; + }; + 7261210D0EE8707500AFED1B /* Products */ = { + isa = PBXGroup; + children = ( + 7261212D0EE8710B00AFED1B /* arp */, + 726121540EE8881700AFED1B /* ifconfig */, + 724DABA20EE88FE3008900D0 /* kdumpd */, + 724DAC0D0EE8940D008900D0 /* ndp */, + 7216D2460EE896C000AE70E4 /* netstat */, + 7216D27C0EE8980A00AE70E4 /* ping */, + 7216D29A0EE898BD00AE70E4 /* ping6 */, + 7216D2CD0EE89B7900AE70E4 /* rarpd */, + 7216D2ED0EE89CBC00AE70E4 /* route */, + 7216D3140EE89E9E00AE70E4 /* rtadvd */, + 7216D3590EE8A02200AE70E4 /* rtsol */, + 7216D3A70EE8A3BB00AE70E4 /* spray */, + 7294F0F90EE8BB460052EC88 /* traceroute */, + 7294F12A0EE8BD280052EC88 /* traceroute6 */, + 4D2B04F31208C2040004A3F3 /* ip6addrctl */, + 690D978112DE6034004323A7 /* mtest */, + 5658259F1339218F003E5FA5 /* mnc */, + 723C7068142BAFEA007C87E9 /* dnctl */, + 7247B83116165EDC00873B3C /* pktapctl */, + 72B732DA1899B0380060E6D4 /* cfilutil */, + 72311F42194A349000EB4788 /* mptcp_client */, + 7200F2FA1958A34D0033E22C /* pktmnglr */, + 713297681A93C743002359CF /* unbound */, + 7282BA0C1AFAD4C9005DE836 /* ecnprobe */, + 72946F431BBF055700087E35 /* frame_delay */, + ); + name = Products; + sourceTree = "<group>"; + }; + 7282BA0D1AFAD4C9005DE836 /* ecnprobe */ = { + isa = PBXGroup; + children = ( + 7282BA341AFAD58E005DE836 /* base.h */, + 7282BA351AFAD58E005DE836 /* capture.c */, + 7282BA361AFAD58E005DE836 /* capture.h */, + 7282BA391AFAD58E005DE836 /* ecn_probe.c */, + 7282BA3A1AFAD58E005DE836 /* ecn.c */, + 7282BA3B1AFAD58E005DE836 /* ecn.h */, + 7282BA3C1AFAD58E005DE836 /* gmt2local.c */, + 7282BA3D1AFAD58E005DE836 /* gmt2local.h */, + 7282BA3E1AFAD58E005DE836 /* history.c */, + 7282BA3F1AFAD58E005DE836 /* history.h */, + 7282BA401AFAD58E005DE836 /* inet.c */, + 7282BA411AFAD58E005DE836 /* inet.h */, + 7282BA451AFAD58E005DE836 /* session.c */, + 7282BA461AFAD58E005DE836 /* session.h */, + 7282BA471AFAD58E005DE836 /* support.c */, + 7282BA481AFAD58E005DE836 /* support.h */, + 7282BA5C1AFBED07005DE836 /* ecnprobe.1 */, + ); + path = ecnprobe; + sourceTree = "<group>"; + }; + 72946F441BBF055700087E35 /* frame_delay */ = { + isa = PBXGroup; + children = ( + 72946F4F1BBF07FF00087E35 /* frame_delay.c */, + 7263724C1BCC718B00E4B026 /* frame_delay.8 */, + ); + path = frame_delay; + sourceTree = "<group>"; + }; + 72B732DB1899B0380060E6D4 /* cfilutil */ = { + isa = PBXGroup; + children = ( + 72B732EE1899B23A0060E6D4 /* cfilutil.c */, + 72B732F01899B2430060E6D4 /* cfilstat.c */, + 72B732DE1899B0380060E6D4 /* cfilutil.1 */, + ); + path = cfilutil; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4D2B04F21208C2040004A3F3 /* ip6addrctl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4D2B05121208C2300004A3F3 /* Build configuration list for PBXNativeTarget "ip6addrctl" */; + buildPhases = ( + 4D2B04F01208C2040004A3F3 /* Sources */, + 4D2B05221208CB410004A3F3 /* CopyFiles */, + 039D6A11120A2CF60006B8C8 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ip6addrctl; + productName = ip6addrctl; + productReference = 4D2B04F31208C2040004A3F3 /* ip6addrctl */; + productType = "com.apple.product-type.tool"; + }; + 5658259E1339218F003E5FA5 /* mnc */ = { + isa = PBXNativeTarget; + buildConfigurationList = 565825AB133921ED003E5FA5 /* Build configuration list for PBXNativeTarget "mnc" */; + buildPhases = ( + 5658259C1339218F003E5FA5 /* Sources */, + 5658259D1339218F003E5FA5 /* Frameworks */, + 565825AA133921ED003E5FA5 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mnc; + productName = mnc; + productReference = 5658259F1339218F003E5FA5 /* mnc */; + productType = "com.apple.product-type.tool"; + }; + 690D978012DE6034004323A7 /* mtest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 690D978612DE6053004323A7 /* Build configuration list for PBXNativeTarget "mtest" */; + buildPhases = ( + 690D977E12DE6034004323A7 /* Sources */, + 690D977F12DE6034004323A7 /* Frameworks */, + 690D97AD12DE7074004323A7 /* CopyFiles */, + 69C10A6312DF7D5300BCDF4C /* Install OSS Plist */, + 69C10A7612DF7EBB00BCDF4C /* Install OSS License */, + 690D97C212DE71CF004323A7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mtest; + productName = mtest; + productReference = 690D978112DE6034004323A7 /* mtest */; + productType = "com.apple.product-type.tool"; + }; + 713297671A93C743002359CF /* unbound */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7132976F1A93C743002359CF /* Build configuration list for PBXNativeTarget "unbound" */; + buildPhases = ( + 713297641A93C743002359CF /* Sources */, + 713297651A93C743002359CF /* Frameworks */, + 713297661A93C743002359CF /* CopyFiles */, + 71D958BB1A9452C200C9B286 /* CopyFiles */, + 71D958BE1A9453A500C9B286 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = unbound; + productName = unbound; + productReference = 713297681A93C743002359CF /* unbound */; + productType = "com.apple.product-type.tool"; + }; + 7200F2F91958A34D0033E22C /* pktmnglr */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7200F3011958A34E0033E22C /* Build configuration list for PBXNativeTarget "pktmnglr" */; + buildPhases = ( + 7200F2F61958A34D0033E22C /* Sources */, + 7200F2F71958A34D0033E22C /* Frameworks */, + 7200F2F81958A34D0033E22C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pktmnglr; + productName = pktmnglr; + productReference = 7200F2FA1958A34D0033E22C /* pktmnglr */; + productType = "com.apple.product-type.tool"; + }; + 7216D2450EE896C000AE70E4 /* netstat */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D24B0EE896EC00AE70E4 /* Build configuration list for PBXNativeTarget "netstat" */; + buildPhases = ( + 7216D2430EE896C000AE70E4 /* Sources */, + 7216D2440EE896C000AE70E4 /* Frameworks */, + 7216D2750EE8979500AE70E4 /* CopyFiles */, + 7216D2640EE8972E00AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = netstat; + productName = netstat; + productReference = 7216D2460EE896C000AE70E4 /* netstat */; + productType = "com.apple.product-type.tool"; + }; + 7216D27B0EE8980A00AE70E4 /* ping */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D2940EE8988200AE70E4 /* Build configuration list for PBXNativeTarget "ping" */; + buildPhases = ( + 7216D2790EE8980A00AE70E4 /* Sources */, + 7216D27A0EE8980A00AE70E4 /* Frameworks */, + 7216D2930EE8988200AE70E4 /* CopyFiles */, + 7216D2870EE8982900AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ping; + productName = ping; + productReference = 7216D27C0EE8980A00AE70E4 /* ping */; + productType = "com.apple.product-type.tool"; + }; + 7216D2990EE898BD00AE70E4 /* ping6 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D2AB0EE8991200AE70E4 /* Build configuration list for PBXNativeTarget "ping6" */; + buildPhases = ( + 7216D2970EE898BD00AE70E4 /* Sources */, + 7216D2980EE898BD00AE70E4 /* Frameworks */, + 7216D2AA0EE8991200AE70E4 /* CopyFiles */, + 7216D2AF0EE8993100AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ping6; + productName = ping6; + productReference = 7216D29A0EE898BD00AE70E4 /* ping6 */; + productType = "com.apple.product-type.tool"; + }; + 7216D2CC0EE89B7900AE70E4 /* rarpd */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D2E30EE89BEA00AE70E4 /* Build configuration list for PBXNativeTarget "rarpd" */; + buildPhases = ( + 7216D2CA0EE89B7900AE70E4 /* Sources */, + 7216D2CB0EE89B7900AE70E4 /* Frameworks */, + 7216D2E20EE89BEA00AE70E4 /* CopyFiles */, + 7216D2D80EE89B9B00AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rarpd; + productName = rarpd; + productReference = 7216D2CD0EE89B7900AE70E4 /* rarpd */; + productType = "com.apple.product-type.tool"; + }; + 7216D2EC0EE89CBC00AE70E4 /* route */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D2FB0EE89D2600AE70E4 /* Build configuration list for PBXNativeTarget "route" */; + buildPhases = ( + 7216D2EA0EE89CBC00AE70E4 /* Sources */, + 7216D2EB0EE89CBC00AE70E4 /* Frameworks */, + 7216D2FA0EE89D2600AE70E4 /* CopyFiles */, + 7216D3030EE89D4300AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = route; + productName = route; + productReference = 7216D2ED0EE89CBC00AE70E4 /* route */; + productType = "com.apple.product-type.tool"; + }; + 7216D3130EE89E9E00AE70E4 /* rtadvd */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D3200EE89EDF00AE70E4 /* Build configuration list for PBXNativeTarget "rtadvd" */; + buildPhases = ( + 7216D3110EE89E9E00AE70E4 /* Sources */, + 7216D3120EE89E9E00AE70E4 /* Frameworks */, + 7216D3370EE89F7500AE70E4 /* CopyFiles */, + 7216D3380EE89F7500AE70E4 /* CopyFiles */, + 7216D3460EE89FD700AE70E4 /* CopyFiles */, + 7216D3450EE89FB900AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rtadvd; + productName = rtadvd; + productReference = 7216D3140EE89E9E00AE70E4 /* rtadvd */; + productType = "com.apple.product-type.tool"; + }; + 7216D3580EE8A02200AE70E4 /* rtsol */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D3660EE8A02B00AE70E4 /* Build configuration list for PBXNativeTarget "rtsol" */; + buildPhases = ( + 7216D3560EE8A02200AE70E4 /* Sources */, + 7216D3570EE8A02200AE70E4 /* Frameworks */, + 7216D37B0EE8A06200AE70E4 /* CopyFiles */, + 7216D3740EE8A06000AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rtsol; + productName = rtsol; + productReference = 7216D3590EE8A02200AE70E4 /* rtsol */; + productType = "com.apple.product-type.tool"; + }; + 7216D3A60EE8A3BA00AE70E4 /* spray */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7216D3C20EE8A42D00AE70E4 /* Build configuration list for PBXNativeTarget "spray" */; + buildPhases = ( + 7216D3A40EE8A3BA00AE70E4 /* Sources */, + 7216D3A50EE8A3BA00AE70E4 /* Frameworks */, + 7216D3C10EE8A42D00AE70E4 /* CopyFiles */, + 7216D3B30EE8A3E000AE70E4 /* ShellScript */, + ); + buildRules = ( + 7216D47D0EE8B84900AE70E4 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = spray; + productName = spray; + productReference = 7216D3A70EE8A3BB00AE70E4 /* spray */; + productType = "com.apple.product-type.tool"; + }; + 72311F41194A349000EB4788 /* mptcp_client */ = { + isa = PBXNativeTarget; + buildConfigurationList = 72311F46194A349100EB4788 /* Build configuration list for PBXNativeTarget "mptcp_client" */; + buildPhases = ( + 72311F3E194A349000EB4788 /* Sources */, + 72311F3F194A349000EB4788 /* Frameworks */, + 72311F40194A349000EB4788 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mptcp_client; + productName = mptcp_client; + productReference = 72311F42194A349000EB4788 /* mptcp_client */; + productType = "com.apple.product-type.tool"; + }; + 723C7067142BAFEA007C87E9 /* dnctl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 723C706F142BAFEA007C87E9 /* Build configuration list for PBXNativeTarget "dnctl" */; + buildPhases = ( + 723C7064142BAFEA007C87E9 /* Sources */, + 723C7065142BAFEA007C87E9 /* Frameworks */, + 723C7066142BAFEA007C87E9 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dnctl; + productName = dnctl; + productReference = 723C7068142BAFEA007C87E9 /* dnctl */; + productType = "com.apple.product-type.tool"; + }; + 7247B83016165EDC00873B3C /* pktapctl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7247B83A16165EDC00873B3C /* Build configuration list for PBXNativeTarget "pktapctl" */; + buildPhases = ( + 7247B82D16165EDC00873B3C /* Sources */, + 7247B82E16165EDC00873B3C /* Frameworks */, + 7247B82F16165EDC00873B3C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pktapctl; + productName = pktapctl; + productReference = 7247B83116165EDC00873B3C /* pktapctl */; + productType = "com.apple.product-type.tool"; + }; + 724DABA10EE88FE3008900D0 /* kdumpd */ = { + isa = PBXNativeTarget; + buildConfigurationList = 724DABB80EE89035008900D0 /* Build configuration list for PBXNativeTarget "kdumpd" */; + buildPhases = ( + 724DAB9F0EE88FE3008900D0 /* Sources */, + 724DABA00EE88FE3008900D0 /* Frameworks */, + 724DABB70EE89035008900D0 /* CopyFiles */, + 724DABC10EE89090008900D0 /* CopyFiles */, + 7216D25F0EE8970100AE70E4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = kdumpd; + productName = kdumpd; + productReference = 724DABA20EE88FE3008900D0 /* kdumpd */; + productType = "com.apple.product-type.tool"; + }; + 724DAC0C0EE8940D008900D0 /* ndp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 724DAC1E0EE894E8008900D0 /* Build configuration list for PBXNativeTarget "ndp" */; + buildPhases = ( + 724DAC0A0EE8940D008900D0 /* Sources */, + 724DAC0B0EE8940D008900D0 /* Frameworks */, + 724DAC1D0EE894E8008900D0 /* CopyFiles */, + 724DAC440EE89562008900D0 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ndp; + productName = ndp; + productReference = 724DAC0D0EE8940D008900D0 /* ndp */; + productType = "com.apple.product-type.tool"; + }; + 7261212C0EE8710B00AFED1B /* arp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 726121400EE8713B00AFED1B /* Build configuration list for PBXNativeTarget "arp" */; + buildPhases = ( + 7261212A0EE8710B00AFED1B /* Sources */, + 7261212B0EE8710B00AFED1B /* Frameworks */, + 7261213F0EE8713B00AFED1B /* CopyFiles */, + 72CD1DA00EE8C4EC005F825D /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = arp; + productName = arp; + productReference = 7261212D0EE8710B00AFED1B /* arp */; + productType = "com.apple.product-type.tool"; + }; + 726121530EE8881700AFED1B /* ifconfig */ = { + isa = PBXNativeTarget; + buildConfigurationList = 726121630EE8887300AFED1B /* Build configuration list for PBXNativeTarget "ifconfig" */; + buildPhases = ( + 726121510EE8881700AFED1B /* Sources */, + 726121520EE8881700AFED1B /* Frameworks */, + 726121620EE8887300AFED1B /* CopyFiles */, + 72CD1DA40EE8C500005F825D /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ifconfig; + productName = ifconfig; + productReference = 726121540EE8881700AFED1B /* ifconfig */; + productType = "com.apple.product-type.tool"; + }; + 7282BA0B1AFAD4C9005DE836 /* ecnprobe */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7282BA131AFAD4C9005DE836 /* Build configuration list for PBXNativeTarget "ecnprobe" */; + buildPhases = ( + 7282BA081AFAD4C9005DE836 /* Sources */, + 7282BA091AFAD4C9005DE836 /* Frameworks */, + 7282BA0A1AFAD4C9005DE836 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ecnprobe; + productName = ecnprobe; + productReference = 7282BA0C1AFAD4C9005DE836 /* ecnprobe */; + productType = "com.apple.product-type.tool"; + }; + 72946F421BBF055700087E35 /* frame_delay */ = { + isa = PBXNativeTarget; + buildConfigurationList = 72946F4A1BBF055700087E35 /* Build configuration list for PBXNativeTarget "frame_delay" */; + buildPhases = ( + 72946F3F1BBF055700087E35 /* Sources */, + 72946F401BBF055700087E35 /* Frameworks */, + 72946F411BBF055700087E35 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = frame_delay; + productName = frame_delay; + productReference = 72946F431BBF055700087E35 /* frame_delay */; + productType = "com.apple.product-type.tool"; + }; + 7294F0F80EE8BB460052EC88 /* traceroute */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7294F0FD0EE8BB550052EC88 /* Build configuration list for PBXNativeTarget "traceroute" */; + buildPhases = ( + 7294F0F60EE8BB460052EC88 /* Sources */, + 7294F0F70EE8BB460052EC88 /* Frameworks */, + 7294F11A0EE8BC0C0052EC88 /* CopyFiles */, + 7294F10C0EE8BBB60052EC88 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = traceroute; + productName = traceroute; + productReference = 7294F0F90EE8BB460052EC88 /* traceroute */; + productType = "com.apple.product-type.tool"; + }; + 7294F1290EE8BD280052EC88 /* traceroute6 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7294F13F0EE8BDB30052EC88 /* Build configuration list for PBXNativeTarget "traceroute6" */; + buildPhases = ( + 7294F1270EE8BD280052EC88 /* Sources */, + 7294F1280EE8BD280052EC88 /* Frameworks */, + 7294F13E0EE8BDB30052EC88 /* CopyFiles */, + 7294F1360EE8BD4D0052EC88 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = traceroute6; + productName = traceroute6; + productReference = 7294F12A0EE8BD280052EC88 /* traceroute6 */; + productType = "com.apple.product-type.tool"; + }; + 72B732D91899B0380060E6D4 /* cfilutil */ = { + isa = PBXNativeTarget; + buildConfigurationList = 72B732E31899B0380060E6D4 /* Build configuration list for PBXNativeTarget "cfilutil" */; + buildPhases = ( + 72B732D61899B0380060E6D4 /* Sources */, + 72B732D71899B0380060E6D4 /* Frameworks */, + 72B732D81899B0380060E6D4 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = cfilutil; + productName = cfilutil; + productReference = 72B732DA1899B0380060E6D4 /* cfilutil */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 724862310EE86EB7001D0DE9 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0800; + TargetAttributes = { + 713297671A93C743002359CF = { + CreatedOnToolsVersion = 6.3; + }; + 7200F2F91958A34D0033E22C = { + CreatedOnToolsVersion = 6.0; + }; + 72311F41194A349000EB4788 = { + CreatedOnToolsVersion = 6.0; + }; + 7282BA0B1AFAD4C9005DE836 = { + CreatedOnToolsVersion = 7.0; + }; + 72946F421BBF055700087E35 = { + CreatedOnToolsVersion = 6.3; + }; + }; + }; + buildConfigurationList = 724862340EE86EB7001D0DE9 /* Build configuration list for PBXProject "network_cmds" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 7248622F0EE86EB7001D0DE9; + productRefGroup = 7261210D0EE8707500AFED1B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 726121430EE8717500AFED1B /* All */, + 72570DA20EE8EBF3000F4CFB /* All-Embedded */, + 72ABD0811083D742008C721C /* All-EmbeddedOther */, + 72C77D3A1484199C002D2577 /* network_cmds_libs */, + 7261212C0EE8710B00AFED1B /* arp */, + 72B732D91899B0380060E6D4 /* cfilutil */, + 723C7067142BAFEA007C87E9 /* dnctl */, + 7282BA0B1AFAD4C9005DE836 /* ecnprobe */, + 72946F421BBF055700087E35 /* frame_delay */, + 726121530EE8881700AFED1B /* ifconfig */, + 4D2B04F21208C2040004A3F3 /* ip6addrctl */, + 724DABA10EE88FE3008900D0 /* kdumpd */, + 5658259E1339218F003E5FA5 /* mnc */, + 72311F41194A349000EB4788 /* mptcp_client */, + 690D978012DE6034004323A7 /* mtest */, + 724DAC0C0EE8940D008900D0 /* ndp */, + 7216D2450EE896C000AE70E4 /* netstat */, + 7247B83016165EDC00873B3C /* pktapctl */, + 7200F2F91958A34D0033E22C /* pktmnglr */, + 7216D27B0EE8980A00AE70E4 /* ping */, + 7216D2990EE898BD00AE70E4 /* ping6 */, + 7216D2CC0EE89B7900AE70E4 /* rarpd */, + 7216D2EC0EE89CBC00AE70E4 /* route */, + 7216D3130EE89E9E00AE70E4 /* rtadvd */, + 7216D3580EE8A02200AE70E4 /* rtsol */, + 7216D3A60EE8A3BA00AE70E4 /* spray */, + 7294F0F80EE8BB460052EC88 /* traceroute */, + 7294F1290EE8BD280052EC88 /* traceroute6 */, + 713297671A93C743002359CF /* unbound */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 039D6A11120A2CF60006B8C8 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/local/share/man/man8/ip6addrctl.8"; + }; + 690D97C212DE71CF004323A7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/local/share/man/man8/mtest.8"; + }; + 69C10A7612DF7EBB00BCDF4C /* Install OSS License */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/mtest.tproj/COPYING", + ); + name = "Install OSS License"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/network_cmds.txt", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "cp \"$SRCROOT/mtest.tproj/COPYING\" \"$DERIVED_FILE_DIR/network_cmds.txt\"\n\nmkdir -p \"$DSTROOT/usr/local/OpenSourceLicenses\"\ncp \"$DERIVED_FILE_DIR/network_cmds.txt\" \"$DSTROOT/usr/local/OpenSourceLicenses/\"\n\n"; + }; + 7216D25F0EE8970100AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/kdumpd.8\n/bin/chmod 0644 $DSTROOT/System/Library/LaunchDaemons/com.apple.kdumpd.plist"; + }; + 7216D2640EE8972E00AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man1/netstat.1\t"; + }; + 7216D2870EE8982900AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/ping.8\t"; + }; + 7216D2AF0EE8993100AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/ping6.8"; + }; + 7216D2D80EE89B9B00AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/rarpd.8"; + }; + 7216D3030EE89D4300AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/route.8"; + }; + 7216D3450EE89FB900AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/rtadvd.8\n/bin/chmod 0444 $DSTROOT/usr/share/man/man5/rtadvd.conf.5\n/bin/chmod 0644 $DSTROOT/private/etc/rtadvd.conf"; + }; + 7216D3740EE8A06000AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/rtsol.8\n/bin/ln -f $DSTROOT/usr/share/man/man8/rtsol.8 $DSTROOT/usr/share/man/man8/rtsold.8\n/bin/ln -f $DSTROOT/sbin/rtsol $DSTROOT/usr/sbin/rtsold\n/usr/bin/strip $DSTROOT/usr/sbin/rtsold\n/bin/chmod 0555 $DSTROOT/usr/sbin/rtsold\n"; + }; + 7216D3B30EE8A3E000AE70E4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/spray.8"; + }; + 724DAC440EE89562008900D0 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/ndp.8\t"; + }; + 7294F10C0EE8BBB60052EC88 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/traceroute.8"; + }; + 7294F1360EE8BD4D0052EC88 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/traceroute6.8"; + }; + 72CD1DA00EE8C4EC005F825D /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/arp.8"; + }; + 72CD1DA40EE8C500005F825D /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "/bin/chmod 0444 $DSTROOT/usr/share/man/man8/ifconfig.8\t"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4D2B04F01208C2040004A3F3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D2B04F81208C21B0004A3F3 /* ip6addrctl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5658259C1339218F003E5FA5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 565825A4133921A3003E5FA5 /* mnc_error.c in Sources */, + 565825A5133921A3003E5FA5 /* mnc_main.c in Sources */, + 565825A6133921A3003E5FA5 /* mnc_multicast.c in Sources */, + 565825A7133921A3003E5FA5 /* mnc_opts.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 690D977E12DE6034004323A7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 690D97A612DE6F96004323A7 /* mtest.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 713297641A93C743002359CF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7200F2F61958A34D0033E22C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7200F2FD1958A34D0033E22C /* packet_mangler.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2430EE896C000AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7216D24C0EE896F300AE70E4 /* data.c in Sources */, + 7216D24D0EE896F300AE70E4 /* if.c in Sources */, + 7216D24E0EE896F300AE70E4 /* inet.c in Sources */, + 7216D24F0EE896F300AE70E4 /* inet6.c in Sources */, + 7216D2500EE896F300AE70E4 /* ipsec.c in Sources */, + 7216D2510EE896F300AE70E4 /* main.c in Sources */, + 01560DFD241969D3001AA29A /* vsock.c in Sources */, + 7216D2520EE896F300AE70E4 /* mbuf.c in Sources */, + 7216D2530EE896F300AE70E4 /* mcast.c in Sources */, + 7218B54A191D4202001B7B52 /* systm.c in Sources */, + 7216D2560EE896F300AE70E4 /* route.c in Sources */, + 7216D2570EE896F300AE70E4 /* tp_astring.c in Sources */, + 56B6B66816F79A1C00D8A7A9 /* mptcp.c in Sources */, + 7216D2580EE896F300AE70E4 /* unix.c in Sources */, + 721654C31EC52447005B17BA /* misc.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2790EE8980A00AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 724769381CE2C1E400AAB5F0 /* gmt2local.c in Sources */, + 7216D2800EE8981C00AE70E4 /* ping.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2970EE898BD00AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 724769371CE2B1FF00AAB5F0 /* gmt2local.c in Sources */, + 7216D2A00EE898DF00AE70E4 /* md5.c in Sources */, + 7216D2A10EE898DF00AE70E4 /* ping6.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2CA0EE89B7900AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7216D2D10EE89B8300AE70E4 /* rarpd.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D2EA0EE89CBC00AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7216D2F20EE89CD600AE70E4 /* route.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D3110EE89E9E00AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72D33F572271319400EF5B5E /* rtadvd_logging.c in Sources */, + 7216D3190EE89EC100AE70E4 /* advcap.c in Sources */, + 7216D31A0EE89EC100AE70E4 /* config.c in Sources */, + 7216D31B0EE89EC100AE70E4 /* dump.c in Sources */, + 7216D31C0EE89EC100AE70E4 /* if.c in Sources */, + 7216D31D0EE89EC100AE70E4 /* rrenum.c in Sources */, + 7216D31E0EE89EC100AE70E4 /* rtadvd.c in Sources */, + 7216D31F0EE89EC100AE70E4 /* timer.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D3560EE8A02200AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7216D3670EE8A04700AE70E4 /* dump.c in Sources */, + 7216D3680EE8A04700AE70E4 /* if.c in Sources */, + 7216D3690EE8A04700AE70E4 /* probe.c in Sources */, + 7216D36A0EE8A04700AE70E4 /* rtsock.c in Sources */, + 7216D36B0EE8A04700AE70E4 /* rtsol.c in Sources */, + 7216D36C0EE8A04700AE70E4 /* rtsold.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7216D3A40EE8A3BA00AE70E4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7216D3AB0EE8A3C400AE70E4 /* spray.c in Sources */, + 7294F0DF0EE8BA730052EC88 /* spray.x in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72311F3E194A349000EB4788 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72311F54194A354F00EB4788 /* conn_lib.c in Sources */, + 72311F55194A354F00EB4788 /* mptcp_client.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 723C7064142BAFEA007C87E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72D000C4142BB11100151981 /* dnctl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7247B82D16165EDC00873B3C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7247B83C16165F0100873B3C /* pktapctl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 724DAB9F0EE88FE3008900D0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 724DABA60EE88FED008900D0 /* kdumpd.c in Sources */, + 724DABA70EE88FED008900D0 /* kdumpsubs.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 724DAC0A0EE8940D008900D0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 724DAC120EE89423008900D0 /* ndp.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7261212A0EE8710B00AFED1B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 726121310EE8711E00AFED1B /* arp.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 726121510EE8881700AFED1B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72B7F36B1BA69352003A9AA2 /* if6lowpan.c in Sources */, + 7261215A0EE8883900AFED1B /* ifbond.c in Sources */, + 7261215B0EE8883900AFED1B /* ifconfig.c in Sources */, + 7261215C0EE8883900AFED1B /* ifmedia.c in Sources */, + 7261215D0EE8883900AFED1B /* ifvlan.c in Sources */, + 72E650A7107BF2F000AAF325 /* af_inet.c in Sources */, + 72E650A8107BF2F000AAF325 /* af_inet6.c in Sources */, + 72E650A9107BF2F000AAF325 /* af_link.c in Sources */, + 72E650AA107BF2F000AAF325 /* ifbridge.c in Sources */, + 72E650AB107BF2F000AAF325 /* ifclone.c in Sources */, + F940359D1E2FF5A900283EB1 /* iffake.c in Sources */, + F97F1E041E9C3FC8002355FF /* nexus.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7282BA081AFAD4C9005DE836 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7282BA531AFAD58E005DE836 /* support.c in Sources */, + 7282BA4D1AFAD58E005DE836 /* gmt2local.c in Sources */, + 7282BA4C1AFAD58E005DE836 /* ecn.c in Sources */, + 7282BA4B1AFAD58E005DE836 /* ecn_probe.c in Sources */, + 7282BA521AFAD58E005DE836 /* session.c in Sources */, + 7282BA491AFAD58E005DE836 /* capture.c in Sources */, + 7282BA4E1AFAD58E005DE836 /* history.c in Sources */, + 7282BA4F1AFAD58E005DE836 /* inet.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72946F3F1BBF055700087E35 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72946F501BBF07FF00087E35 /* frame_delay.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7294F0F60EE8BB460052EC88 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7294F1000EE8BB990052EC88 /* as.c in Sources */, + 7294F1010EE8BB990052EC88 /* findsaddr-socket.c in Sources */, + 7294F1020EE8BB990052EC88 /* ifaddrlist.c in Sources */, + 7294F1030EE8BB990052EC88 /* traceroute.c in Sources */, + 7294F1040EE8BB990052EC88 /* version.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7294F1270EE8BD280052EC88 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7294F12E0EE8BD2F0052EC88 /* traceroute6.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72B732D61899B0380060E6D4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72B732EF1899B23A0060E6D4 /* cfilutil.c in Sources */, + 72B732F11899B2430060E6D4 /* cfilstat.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 034E4464100BDCA3009CA3DC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7261212C0EE8710B00AFED1B /* arp */; + targetProxy = 034E4463100BDCA3009CA3DC /* PBXContainerItemProxy */; + }; + 034E4469100BDD00009CA3DC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 726121530EE8881700AFED1B /* ifconfig */; + targetProxy = 034E4468100BDD00009CA3DC /* PBXContainerItemProxy */; + }; + 034E4475100BDEC6009CA3DC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2450EE896C000AE70E4 /* netstat */; + targetProxy = 034E4474100BDEC6009CA3DC /* PBXContainerItemProxy */; + }; + 034E447B100BDF0D009CA3DC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D27B0EE8980A00AE70E4 /* ping */; + targetProxy = 034E447A100BDF0D009CA3DC /* PBXContainerItemProxy */; + }; + 034E447F100BDF54009CA3DC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2EC0EE89CBC00AE70E4 /* route */; + targetProxy = 034E447E100BDF54009CA3DC /* PBXContainerItemProxy */; + }; + 034E4485100BE15F009CA3DC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7294F0F80EE8BB460052EC88 /* traceroute */; + targetProxy = 034E4484100BE15F009CA3DC /* PBXContainerItemProxy */; + }; + 03B2DBD1100BE626005349BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 724DAC0C0EE8940D008900D0 /* ndp */; + targetProxy = 03B2DBD0100BE626005349BC /* PBXContainerItemProxy */; + }; + 03B2DBD3100BE645005349BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2990EE898BD00AE70E4 /* ping6 */; + targetProxy = 03B2DBD2100BE645005349BC /* PBXContainerItemProxy */; + }; + 03B2DBDB100BE6D2005349BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3580EE8A02200AE70E4 /* rtsol */; + targetProxy = 03B2DBDA100BE6D2005349BC /* PBXContainerItemProxy */; + }; + 03B2DBDD100BE6D5005349BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7294F1290EE8BD280052EC88 /* traceroute6 */; + targetProxy = 03B2DBDC100BE6D5005349BC /* PBXContainerItemProxy */; + }; + 18515B85133D1DBF000148A4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3130EE89E9E00AE70E4 /* rtadvd */; + targetProxy = 18515B84133D1DBF000148A4 /* PBXContainerItemProxy */; + }; + 4D2B05141208C6BB0004A3F3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4D2B04F21208C2040004A3F3 /* ip6addrctl */; + targetProxy = 4D2B05131208C6BB0004A3F3 /* PBXContainerItemProxy */; + }; + 565825AD13392232003E5FA5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5658259E1339218F003E5FA5 /* mnc */; + targetProxy = 565825AC13392232003E5FA5 /* PBXContainerItemProxy */; + }; + 565825AF13392239003E5FA5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5658259E1339218F003E5FA5 /* mnc */; + targetProxy = 565825AE13392239003E5FA5 /* PBXContainerItemProxy */; + }; + 565825B113392242003E5FA5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5658259E1339218F003E5FA5 /* mnc */; + targetProxy = 565825B013392242003E5FA5 /* PBXContainerItemProxy */; + }; + 690D97BA12DE7130004323A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 690D978012DE6034004323A7 /* mtest */; + targetProxy = 690D97B912DE7130004323A7 /* PBXContainerItemProxy */; + }; + 690D97BC12DE7151004323A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 690D978012DE6034004323A7 /* mtest */; + targetProxy = 690D97BB12DE7151004323A7 /* PBXContainerItemProxy */; + }; + 690D97BE12DE7166004323A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 690D978012DE6034004323A7 /* mtest */; + targetProxy = 690D97BD12DE7166004323A7 /* PBXContainerItemProxy */; + }; + 71D958C51A9455A000C9B286 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 713297671A93C743002359CF /* unbound */; + targetProxy = 71D958C41A9455A000C9B286 /* PBXContainerItemProxy */; + }; + 7200F3031958A4F10033E22C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7200F2F91958A34D0033E22C /* pktmnglr */; + targetProxy = 7200F3021958A4F10033E22C /* PBXContainerItemProxy */; + }; + 7200F3051958A4FA0033E22C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7200F2F91958A34D0033E22C /* pktmnglr */; + targetProxy = 7200F3041958A4FA0033E22C /* PBXContainerItemProxy */; + }; + 7200F3071958A5040033E22C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7200F2F91958A34D0033E22C /* pktmnglr */; + targetProxy = 7200F3061958A5040033E22C /* PBXContainerItemProxy */; + }; + 7216D2670EE8978F00AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2450EE896C000AE70E4 /* netstat */; + targetProxy = 7216D2660EE8978F00AE70E4 /* PBXContainerItemProxy */; + }; + 7216D2C00EE89ADF00AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D27B0EE8980A00AE70E4 /* ping */; + targetProxy = 7216D2BF0EE89ADF00AE70E4 /* PBXContainerItemProxy */; + }; + 7216D2C20EE89ADF00AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2990EE898BD00AE70E4 /* ping6 */; + targetProxy = 7216D2C10EE89ADF00AE70E4 /* PBXContainerItemProxy */; + }; + 7216D2DA0EE89BE900AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2CC0EE89B7900AE70E4 /* rarpd */; + targetProxy = 7216D2D90EE89BE900AE70E4 /* PBXContainerItemProxy */; + }; + 7216D3060EE89D9A00AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2EC0EE89CBC00AE70E4 /* route */; + targetProxy = 7216D3050EE89D9A00AE70E4 /* PBXContainerItemProxy */; + }; + 7216D34D0EE89FEC00AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3130EE89E9E00AE70E4 /* rtadvd */; + targetProxy = 7216D34C0EE89FEC00AE70E4 /* PBXContainerItemProxy */; + }; + 7216D37F0EE8A0B300AE70E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3580EE8A02200AE70E4 /* rtsol */; + targetProxy = 7216D37E0EE8A0B300AE70E4 /* PBXContainerItemProxy */; + }; + 72179EAE146233390098FB3E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 723C7067142BAFEA007C87E9 /* dnctl */; + targetProxy = 72179EAD146233390098FB3E /* PBXContainerItemProxy */; + }; + 72311F4B194A34EB00EB4788 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72311F41194A349000EB4788 /* mptcp_client */; + targetProxy = 72311F4A194A34EB00EB4788 /* PBXContainerItemProxy */; + }; + 72311F4D194A34F500EB4788 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72311F41194A349000EB4788 /* mptcp_client */; + targetProxy = 72311F4C194A34F500EB4788 /* PBXContainerItemProxy */; + }; + 72311F4F194A34FE00EB4788 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72311F41194A349000EB4788 /* mptcp_client */; + targetProxy = 72311F4E194A34FE00EB4788 /* PBXContainerItemProxy */; + }; + 723C7074142BB003007C87E9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 723C7067142BAFEA007C87E9 /* dnctl */; + targetProxy = 723C7073142BB003007C87E9 /* PBXContainerItemProxy */; + }; + 724DABC30EE890A6008900D0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 724DABA10EE88FE3008900D0 /* kdumpd */; + targetProxy = 724DABC20EE890A6008900D0 /* PBXContainerItemProxy */; + }; + 724DAC240EE89525008900D0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 724DAC0C0EE8940D008900D0 /* ndp */; + targetProxy = 724DAC230EE89525008900D0 /* PBXContainerItemProxy */; + }; + 7250E1471616642000A11A76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7247B83016165EDC00873B3C /* pktapctl */; + targetProxy = 7250E1461616642000A11A76 /* PBXContainerItemProxy */; + }; + 7250E1491616642900A11A76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7247B83016165EDC00873B3C /* pktapctl */; + targetProxy = 7250E1481616642900A11A76 /* PBXContainerItemProxy */; + }; + 7250E14B1616643000A11A76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7247B83016165EDC00873B3C /* pktapctl */; + targetProxy = 7250E14A1616643000A11A76 /* PBXContainerItemProxy */; + }; + 726121490EE8717B00AFED1B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7261212C0EE8710B00AFED1B /* arp */; + targetProxy = 726121480EE8717B00AFED1B /* PBXContainerItemProxy */; + }; + 7261217D0EE8896800AFED1B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 726121530EE8881700AFED1B /* ifconfig */; + targetProxy = 7261217C0EE8896800AFED1B /* PBXContainerItemProxy */; + }; + 7263724B1BCC709900E4B026 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72946F421BBF055700087E35 /* frame_delay */; + targetProxy = 7263724A1BCC709900E4B026 /* PBXContainerItemProxy */; + }; + 7282BA571AFBDEAD005DE836 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7282BA0B1AFAD4C9005DE836 /* ecnprobe */; + targetProxy = 7282BA561AFBDEAD005DE836 /* PBXContainerItemProxy */; + }; + 7282BA591AFBDEC2005DE836 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7282BA0B1AFAD4C9005DE836 /* ecnprobe */; + targetProxy = 7282BA581AFBDEC2005DE836 /* PBXContainerItemProxy */; + }; + 7282BA5B1AFBDED3005DE836 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7282BA0B1AFAD4C9005DE836 /* ecnprobe */; + targetProxy = 7282BA5A1AFBDED3005DE836 /* PBXContainerItemProxy */; + }; + 72946F4C1BBF063800087E35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72946F421BBF055700087E35 /* frame_delay */; + targetProxy = 72946F4B1BBF063800087E35 /* PBXContainerItemProxy */; + }; + 72946F4E1BBF063F00087E35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72946F421BBF055700087E35 /* frame_delay */; + targetProxy = 72946F4D1BBF063F00087E35 /* PBXContainerItemProxy */; + }; + 7294F0EA0EE8BAC80052EC88 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3A60EE8A3BA00AE70E4 /* spray */; + targetProxy = 7294F0E90EE8BAC80052EC88 /* PBXContainerItemProxy */; + }; + 7294F1210EE8BCC20052EC88 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7294F0F80EE8BB460052EC88 /* traceroute */; + targetProxy = 7294F1200EE8BCC20052EC88 /* PBXContainerItemProxy */; + }; + 72ABD0881083D750008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 726121530EE8881700AFED1B /* ifconfig */; + targetProxy = 72ABD0871083D750008C721C /* PBXContainerItemProxy */; + }; + 72ABD08C1083D75D008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 724DAC0C0EE8940D008900D0 /* ndp */; + targetProxy = 72ABD08B1083D75D008C721C /* PBXContainerItemProxy */; + }; + 72ABD08E1083D75F008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2450EE896C000AE70E4 /* netstat */; + targetProxy = 72ABD08D1083D75F008C721C /* PBXContainerItemProxy */; + }; + 72ABD0901083D762008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D27B0EE8980A00AE70E4 /* ping */; + targetProxy = 72ABD08F1083D762008C721C /* PBXContainerItemProxy */; + }; + 72ABD0921083D764008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2990EE898BD00AE70E4 /* ping6 */; + targetProxy = 72ABD0911083D764008C721C /* PBXContainerItemProxy */; + }; + 72ABD0941083D767008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2CC0EE89B7900AE70E4 /* rarpd */; + targetProxy = 72ABD0931083D767008C721C /* PBXContainerItemProxy */; + }; + 72ABD0961083D76A008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D2EC0EE89CBC00AE70E4 /* route */; + targetProxy = 72ABD0951083D76A008C721C /* PBXContainerItemProxy */; + }; + 72ABD0981083D76D008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3130EE89E9E00AE70E4 /* rtadvd */; + targetProxy = 72ABD0971083D76D008C721C /* PBXContainerItemProxy */; + }; + 72ABD09A1083D76F008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7216D3580EE8A02200AE70E4 /* rtsol */; + targetProxy = 72ABD0991083D76F008C721C /* PBXContainerItemProxy */; + }; + 72ABD09C1083D772008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7294F0F80EE8BB460052EC88 /* traceroute */; + targetProxy = 72ABD09B1083D772008C721C /* PBXContainerItemProxy */; + }; + 72ABD09E1083D774008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7294F1290EE8BD280052EC88 /* traceroute6 */; + targetProxy = 72ABD09D1083D774008C721C /* PBXContainerItemProxy */; + }; + 72ABD0A41083D818008C721C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7261212C0EE8710B00AFED1B /* arp */; + targetProxy = 72ABD0A31083D818008C721C /* PBXContainerItemProxy */; + }; + 72B732E91899B18F0060E6D4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72B732D91899B0380060E6D4 /* cfilutil */; + targetProxy = 72B732E81899B18F0060E6D4 /* PBXContainerItemProxy */; + }; + 72B732EB1899B19A0060E6D4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72B732D91899B0380060E6D4 /* cfilutil */; + targetProxy = 72B732EA1899B19A0060E6D4 /* PBXContainerItemProxy */; + }; + 72CD1D9C0EE8C47C005F825D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7294F1290EE8BD280052EC88 /* traceroute6 */; + targetProxy = 72CD1D9B0EE8C47C005F825D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 03B2DBE2100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + "DEBUG_INFORMATION_FORMAT[sdk=iphoneos*][arch=*]" = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = ( + "USE_RFC2292BIS=1", + "__APPLE_USE_RFC_3542=1", + "__APPLE_API_OBSOLETE=1", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + PREBINDING = NO; + SDKROOT = macosx.internal; + "STRIPFLAGS[sdk=iphoneos*]" = "-S"; + SUPPORTED_PLATFORMS = "macosx iphoneos"; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; + }; + name = "Ignore Me"; + }; + 03B2DBE3100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = network_cmds; + ZERO_LINK = NO; + }; + name = "Ignore Me"; + }; + 03B2DBE4100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = network_cmds; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Ignore Me"; + }; + 03B2DBE6100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = arp; + }; + name = "Ignore Me"; + }; + 03B2DBE7100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + USE_IF_MEDIA, + NO_IPX, + INET6, + ); + "GCC_PREPROCESSOR_DEFINITIONS[sdk=macosx*][arch=*]" = ( + "$(inherited)", + USE_VLANS, + USE_BONDS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ifconfig; + ZERO_LINK = NO; + }; + name = "Ignore Me"; + }; + 03B2DBEB100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/libexec; + PRODUCT_NAME = kdumpd; + }; + name = "Ignore Me"; + }; + 03B2DBED100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC_DEBUG, + KAME_SCOPEID, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = ndp; + }; + name = "Ignore Me"; + }; + 03B2DBEE100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + $inherited, + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = netstat; + }; + name = "Ignore Me"; + }; + 03B2DBEF100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ping; + }; + name = "Ignore Me"; + }; + 03B2DBF0100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ping6; + }; + name = "Ignore Me"; + }; + 03B2DBF1100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "TFTP_DIR=\\\"/tftpboot\\\"", + ); + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = rarpd; + }; + name = "Ignore Me"; + }; + 03B2DBF2100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = route; + }; + name = "Ignore Me"; + }; + 03B2DBF3100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + HAVE_GETIFADDRS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = rtadvd; + USE_HEADERMAP = NO; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-deprecated-declarations", + "-Wno-address-of-packed-member", + ); + }; + name = "Ignore Me"; + }; + 03B2DBF4100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + HAVE_GETIFADDRS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = rtsol; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-deprecated-declarations", + ); + }; + name = "Ignore Me"; + }; + 03B2DBF5100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = spray; + }; + name = "Ignore Me"; + }; + 03B2DBF6100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALTERNATE_MODE = 04555; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + HAVE_SOCKADDR_SA_LEN, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 04555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = traceroute; + }; + name = "Ignore Me"; + }; + 03B2DBF7100BE71D005349BC /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 04555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = traceroute6; + }; + name = "Ignore Me"; + }; + 4D2B04F51208C2050004A3F3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALTERNATE_MODE = 0555; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = ip6addrctl; + }; + name = Debug; + }; + 4D2B04F61208C2050004A3F3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = ip6addrctl; + ZERO_LINK = NO; + }; + name = Release; + }; + 4D2B04F71208C2050004A3F3 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = ip6addrctl; + }; + name = "Ignore Me"; + }; + 565825A11339218F003E5FA5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = mnc; + }; + name = Debug; + }; + 565825A21339218F003E5FA5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = mnc; + ZERO_LINK = NO; + }; + name = Release; + }; + 565825A31339218F003E5FA5 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = mnc; + }; + name = "Ignore Me"; + }; + 690D978312DE6035004323A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + "DEBUG_INFORMATION_FORMAT[sdk=iphoneos*][arch=*]" = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = mtest; + }; + name = Debug; + }; + 690D978412DE6035004323A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + "DEBUG_INFORMATION_FORMAT[sdk=iphoneos*][arch=*]" = dwarf; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = mtest; + ZERO_LINK = NO; + }; + name = Release; + }; + 690D978512DE6035004323A7 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + "DEBUG_INFORMATION_FORMAT[sdk=iphoneos*][arch=*]" = dwarf; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = mtest; + }; + name = "Ignore Me"; + }; + 7132976C1A93C743002359CF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "-"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + HEADER_SEARCH_PATHS = /usr/local/libressl/include; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/libexec; + LIBRARY_SEARCH_PATHS = /usr/local/libressl/lib; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ( + "-lssl", + "-lcrypto", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/unbound"; + USE_HEADERMAP = NO; + VALID_ARCHS = "x86_64 x86_64h"; + }; + name = Debug; + }; + 7132976D1A93C743002359CF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "-"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + HEADER_SEARCH_PATHS = /usr/local/libressl/include; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/libexec; + LIBRARY_SEARCH_PATHS = /usr/local/libressl/lib; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ( + "-lssl", + "-lcrypto", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/unbound"; + USE_HEADERMAP = NO; + VALID_ARCHS = "x86_64 x86_64h"; + }; + name = Release; + }; + 7132976E1A93C743002359CF /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "-"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + HEADER_SEARCH_PATHS = /usr/local/libressl/include; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/libexec; + LIBRARY_SEARCH_PATHS = /usr/local/libressl/lib; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ( + "-lssl", + "-lcrypto", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/unbound"; + USE_HEADERMAP = NO; + VALID_ARCHS = "x86_64 x86_64h"; + }; + name = "Ignore Me"; + }; + 7200F2FE1958A34D0033E22C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 7200F2FF1958A34D0033E22C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 7200F3001958A34D0033E22C /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 7216D2480EE896C100AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + $inherited, + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = netstat; + }; + name = Debug; + }; + 7216D2490EE896C100AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + $inherited, + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = netstat; + }; + name = Release; + }; + 7216D27E0EE8980B00AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "USE_RFC2292BIS=1", + "__APPLE_USE_RFC_3542=1", + "__APPLE_API_OBSOLETE=1", + "DEBUG=1", + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ping; + }; + name = Debug; + }; + 7216D27F0EE8980B00AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ping; + }; + name = Release; + }; + 7216D29C0EE898BE00AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ping6; + }; + name = Debug; + }; + 7216D29D0EE898BE00AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ping6; + }; + name = Release; + }; + 7216D2CF0EE89B7A00AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "TFTP_DIR=\\\"/tftpboot\\\"", + ); + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = rarpd; + }; + name = Debug; + }; + 7216D2D00EE89B7A00AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "TFTP_DIR=\\\"/tftpboot\\\"", + ); + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = rarpd; + }; + name = Release; + }; + 7216D2EF0EE89CBC00AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = route; + }; + name = Debug; + }; + 7216D2F00EE89CBC00AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = route; + }; + name = Release; + }; + 7216D3160EE89E9F00AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + HAVE_GETIFADDRS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = rtadvd; + USE_HEADERMAP = NO; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-deprecated-declarations", + "-Wno-address-of-packed-member", + ); + }; + name = Debug; + }; + 7216D3170EE89E9F00AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + HAVE_GETIFADDRS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = rtadvd; + USE_HEADERMAP = NO; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-deprecated-declarations", + "-Wno-address-of-packed-member", + ); + }; + name = Release; + }; + 7216D35B0EE8A02300AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + HAVE_GETIFADDRS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = rtsol; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-deprecated-declarations", + ); + }; + name = Debug; + }; + 7216D35C0EE8A02300AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + HAVE_GETIFADDRS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = rtsol; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-deprecated-declarations", + ); + }; + name = Release; + }; + 7216D3A90EE8A3BB00AE70E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = spray; + }; + name = Debug; + }; + 7216D3AA0EE8A3BB00AE70E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = spray; + }; + name = Release; + }; + 72311F47194A349100EB4788 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 72311F48194A349100EB4788 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 72311F49194A349100EB4788 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 723C7070142BAFEA007C87E9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 723C7071142BAFEA007C87E9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 723C7072142BAFEA007C87E9 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 7247B83716165EDC00873B3C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 7247B83816165EDC00873B3C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 7247B83916165EDC00873B3C /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 724862320EE86EB7001D0DE9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPRESSION = lossless; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + ENABLE_TESTABILITY = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "USE_RFC2292BIS=1", + "__APPLE_USE_RFC_3542=1", + "__APPLE_API_OBSOLETE=1", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + PREBINDING = NO; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "macosx iphoneos"; + WARNING_CFLAGS = "-Wall"; + }; + name = Debug; + }; + 724862330EE86EB7001D0DE9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPRESSION = "respect-asset-catalog"; + COPY_PHASE_STRIP = YES; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + "DEBUG_INFORMATION_FORMAT[sdk=iphoneos*][arch=*]" = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = ( + "USE_RFC2292BIS=1", + "__APPLE_USE_RFC_3542=1", + "__APPLE_API_OBSOLETE=1", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + PREBINDING = NO; + SDKROOT = macosx.internal; + "STRIPFLAGS[sdk=iphoneos*]" = "-S"; + SUPPORTED_PLATFORMS = "macosx iphoneos"; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; + }; + name = Release; + }; + 724DABA40EE88FE3008900D0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/libexec; + PRODUCT_NAME = kdumpd; + }; + name = Debug; + }; + 724DABA50EE88FE3008900D0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/libexec; + PRODUCT_NAME = kdumpd; + }; + name = Release; + }; + 724DAC0F0EE8940E008900D0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC_DEBUG, + KAME_SCOPEID, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = ndp; + }; + name = Debug; + }; + 724DAC100EE8940E008900D0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC_DEBUG, + KAME_SCOPEID, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = ndp; + }; + name = Release; + }; + 72570DA30EE8EBF3000F4CFB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = network_cmds; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 72570DA40EE8EBF3000F4CFB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = network_cmds; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 7261212F0EE8710B00AFED1B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = arp; + }; + name = Debug; + }; + 726121300EE8710B00AFED1B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = arp; + }; + name = Release; + }; + 726121440EE8717500AFED1B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = network_cmds; + }; + name = Debug; + }; + 726121450EE8717500AFED1B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = network_cmds; + ZERO_LINK = NO; + }; + name = Release; + }; + 726121560EE8881800AFED1B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + USE_IF_MEDIA, + NO_IPX, + INET6, + ); + "GCC_PREPROCESSOR_DEFINITIONS[sdk=macosx*][arch=*]" = ( + $inherited, + USE_VLANS, + USE_BONDS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ifconfig; + }; + name = Debug; + }; + 726121570EE8881800AFED1B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + USE_IF_MEDIA, + NO_IPX, + INET6, + ); + "GCC_PREPROCESSOR_DEFINITIONS[sdk=macosx*][arch=*]" = ( + "$(inherited)", + USE_VLANS, + USE_BONDS, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + INSTALL_PATH = /sbin; + PRODUCT_NAME = ifconfig; + ZERO_LINK = NO; + }; + name = Release; + }; + 7282BA101AFAD4C9005DE836 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 7282BA111AFAD4C9005DE836 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 7282BA121AFAD4C9005DE836 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 72946F471BBF055700087E35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.11.xctoolchain/usr/include, + /System/Library/Frameworks/System.framework/PrivateHeaders, + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 72946F481BBF055700087E35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.11.xctoolchain/usr/include, + /System/Library/Frameworks/System.framework/PrivateHeaders, + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 72946F491BBF055700087E35 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.11.xctoolchain/usr/include, + /System/Library/Frameworks/System.framework/PrivateHeaders, + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 7294F0FB0EE8BB460052EC88 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALTERNATE_MODE = 04555; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + HAVE_SOCKADDR_SA_LEN, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 04555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = traceroute; + }; + name = Debug; + }; + 7294F0FC0EE8BB460052EC88 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALTERNATE_MODE = 04555; + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + HAVE_SOCKADDR_SA_LEN, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 04555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = traceroute; + }; + name = Release; + }; + 7294F12C0EE8BD290052EC88 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 04555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = traceroute6; + }; + name = Debug; + }; + 7294F12D0EE8BD290052EC88 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/network-client-server-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + INET6, + IPSEC, + ); + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 04555; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = traceroute6; + }; + name = Release; + }; + 72ABD0821083D743008C721C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = "All-EmbeddedOther"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 72ABD0831083D743008C721C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = "All-EmbeddedOther"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + TARGETED_DEVICE_FAMILY = "1,2"; + ZERO_LINK = NO; + }; + name = Release; + }; + 72ABD0841083D743008C721C /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "All-EmbeddedOther"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Ignore Me"; + }; + 72B732E01899B0380060E6D4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + MACOSX_DEPLOYMENT_TARGET = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 72B732E11899B0380060E6D4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + MACOSX_DEPLOYMENT_TARGET = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 72B732E21899B0380060E6D4 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_MODE_FLAG = 0555; + MACOSX_DEPLOYMENT_TARGET = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = "Ignore Me"; + }; + 72C77D681484199C002D2577 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = network_cmds_libs; + }; + name = Debug; + }; + 72C77D691484199C002D2577 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = network_cmds_libs; + ZERO_LINK = NO; + }; + name = Release; + }; + 72C77D6A1484199C002D2577 /* Ignore Me */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = network_cmds_libs; + ZERO_LINK = NO; + }; + name = "Ignore Me"; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4D2B05121208C2300004A3F3 /* Build configuration list for PBXNativeTarget "ip6addrctl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4D2B04F51208C2050004A3F3 /* Debug */, + 4D2B04F61208C2050004A3F3 /* Release */, + 4D2B04F71208C2050004A3F3 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 565825AB133921ED003E5FA5 /* Build configuration list for PBXNativeTarget "mnc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 565825A11339218F003E5FA5 /* Debug */, + 565825A21339218F003E5FA5 /* Release */, + 565825A31339218F003E5FA5 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 690D978612DE6053004323A7 /* Build configuration list for PBXNativeTarget "mtest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 690D978312DE6035004323A7 /* Debug */, + 690D978412DE6035004323A7 /* Release */, + 690D978512DE6035004323A7 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7132976F1A93C743002359CF /* Build configuration list for PBXNativeTarget "unbound" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7132976C1A93C743002359CF /* Debug */, + 7132976D1A93C743002359CF /* Release */, + 7132976E1A93C743002359CF /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7200F3011958A34E0033E22C /* Build configuration list for PBXNativeTarget "pktmnglr" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7200F2FE1958A34D0033E22C /* Debug */, + 7200F2FF1958A34D0033E22C /* Release */, + 7200F3001958A34D0033E22C /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D24B0EE896EC00AE70E4 /* Build configuration list for PBXNativeTarget "netstat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D2480EE896C100AE70E4 /* Debug */, + 7216D2490EE896C100AE70E4 /* Release */, + 03B2DBEE100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D2940EE8988200AE70E4 /* Build configuration list for PBXNativeTarget "ping" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D27E0EE8980B00AE70E4 /* Debug */, + 7216D27F0EE8980B00AE70E4 /* Release */, + 03B2DBEF100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D2AB0EE8991200AE70E4 /* Build configuration list for PBXNativeTarget "ping6" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D29C0EE898BE00AE70E4 /* Debug */, + 7216D29D0EE898BE00AE70E4 /* Release */, + 03B2DBF0100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D2E30EE89BEA00AE70E4 /* Build configuration list for PBXNativeTarget "rarpd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D2CF0EE89B7A00AE70E4 /* Debug */, + 7216D2D00EE89B7A00AE70E4 /* Release */, + 03B2DBF1100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D2FB0EE89D2600AE70E4 /* Build configuration list for PBXNativeTarget "route" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D2EF0EE89CBC00AE70E4 /* Debug */, + 7216D2F00EE89CBC00AE70E4 /* Release */, + 03B2DBF2100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D3200EE89EDF00AE70E4 /* Build configuration list for PBXNativeTarget "rtadvd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D3160EE89E9F00AE70E4 /* Debug */, + 7216D3170EE89E9F00AE70E4 /* Release */, + 03B2DBF3100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D3660EE8A02B00AE70E4 /* Build configuration list for PBXNativeTarget "rtsol" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D35B0EE8A02300AE70E4 /* Debug */, + 7216D35C0EE8A02300AE70E4 /* Release */, + 03B2DBF4100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7216D3C20EE8A42D00AE70E4 /* Build configuration list for PBXNativeTarget "spray" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7216D3A90EE8A3BB00AE70E4 /* Debug */, + 7216D3AA0EE8A3BB00AE70E4 /* Release */, + 03B2DBF5100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72311F46194A349100EB4788 /* Build configuration list for PBXNativeTarget "mptcp_client" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72311F47194A349100EB4788 /* Debug */, + 72311F48194A349100EB4788 /* Release */, + 72311F49194A349100EB4788 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 723C706F142BAFEA007C87E9 /* Build configuration list for PBXNativeTarget "dnctl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 723C7070142BAFEA007C87E9 /* Debug */, + 723C7071142BAFEA007C87E9 /* Release */, + 723C7072142BAFEA007C87E9 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7247B83A16165EDC00873B3C /* Build configuration list for PBXNativeTarget "pktapctl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7247B83716165EDC00873B3C /* Debug */, + 7247B83816165EDC00873B3C /* Release */, + 7247B83916165EDC00873B3C /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 724862340EE86EB7001D0DE9 /* Build configuration list for PBXProject "network_cmds" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 724862320EE86EB7001D0DE9 /* Debug */, + 724862330EE86EB7001D0DE9 /* Release */, + 03B2DBE2100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 724DABB80EE89035008900D0 /* Build configuration list for PBXNativeTarget "kdumpd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 724DABA40EE88FE3008900D0 /* Debug */, + 724DABA50EE88FE3008900D0 /* Release */, + 03B2DBEB100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 724DAC1E0EE894E8008900D0 /* Build configuration list for PBXNativeTarget "ndp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 724DAC0F0EE8940E008900D0 /* Debug */, + 724DAC100EE8940E008900D0 /* Release */, + 03B2DBED100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72570DA60EE8EC0F000F4CFB /* Build configuration list for PBXAggregateTarget "All-Embedded" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72570DA30EE8EBF3000F4CFB /* Debug */, + 72570DA40EE8EBF3000F4CFB /* Release */, + 03B2DBE4100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 726121400EE8713B00AFED1B /* Build configuration list for PBXNativeTarget "arp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7261212F0EE8710B00AFED1B /* Debug */, + 726121300EE8710B00AFED1B /* Release */, + 03B2DBE6100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7261214B0EE8717D00AFED1B /* Build configuration list for PBXAggregateTarget "All" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 726121440EE8717500AFED1B /* Debug */, + 726121450EE8717500AFED1B /* Release */, + 03B2DBE3100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 726121630EE8887300AFED1B /* Build configuration list for PBXNativeTarget "ifconfig" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 726121560EE8881800AFED1B /* Debug */, + 726121570EE8881800AFED1B /* Release */, + 03B2DBE7100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7282BA131AFAD4C9005DE836 /* Build configuration list for PBXNativeTarget "ecnprobe" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7282BA101AFAD4C9005DE836 /* Debug */, + 7282BA111AFAD4C9005DE836 /* Release */, + 7282BA121AFAD4C9005DE836 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72946F4A1BBF055700087E35 /* Build configuration list for PBXNativeTarget "frame_delay" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72946F471BBF055700087E35 /* Debug */, + 72946F481BBF055700087E35 /* Release */, + 72946F491BBF055700087E35 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7294F0FD0EE8BB550052EC88 /* Build configuration list for PBXNativeTarget "traceroute" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7294F0FB0EE8BB460052EC88 /* Debug */, + 7294F0FC0EE8BB460052EC88 /* Release */, + 03B2DBF6100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7294F13F0EE8BDB30052EC88 /* Build configuration list for PBXNativeTarget "traceroute6" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7294F12C0EE8BD290052EC88 /* Debug */, + 7294F12D0EE8BD290052EC88 /* Release */, + 03B2DBF7100BE71D005349BC /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72ABD0A11083D792008C721C /* Build configuration list for PBXAggregateTarget "All-EmbeddedOther" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72ABD0821083D743008C721C /* Debug */, + 72ABD0831083D743008C721C /* Release */, + 72ABD0841083D743008C721C /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72B732E31899B0380060E6D4 /* Build configuration list for PBXNativeTarget "cfilutil" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72B732E01899B0380060E6D4 /* Debug */, + 72B732E11899B0380060E6D4 /* Release */, + 72B732E21899B0380060E6D4 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 72C77D671484199C002D2577 /* Build configuration list for PBXAggregateTarget "network_cmds_libs" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72C77D681484199C002D2577 /* Debug */, + 72C77D691484199C002D2577 /* Release */, + 72C77D6A1484199C002D2577 /* Ignore Me */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 724862310EE86EB7001D0DE9 /* Project object */; +} diff --git a/network_cmds/ping.tproj/ping.8 b/network_cmds/ping.tproj/ping.8 new file mode 100644 index 0000000..a2955a6 --- /dev/null +++ b/network_cmds/ping.tproj/ping.8 @@ -0,0 +1,616 @@ +.\" Copyright (c) 1999-2013 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ping.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd March 29, 2013 +.Dt PING 8 +.Os +.Sh NAME +.Nm ping +.Nd send +.Tn ICMP ECHO_REQUEST +packets to network hosts +.Sh SYNOPSIS +.Nm +.Op Fl AaCDdfnoQqRrv +.Op Fl b Ar boundif +.Op Fl c Ar count +.Op Fl G Ar sweepmaxsize +.Op Fl g Ar sweepminsize +.Op Fl h Ar sweepincrsize +.Op Fl i Ar wait +.Op Fl k Ar trafficclass +.Op Fl K Ar netservicetype +.Op Fl l Ar preload +.Op Fl M Cm mask | time +.Op Fl m Ar ttl +.Op Fl P Ar policy +.Op Fl p Ar pattern +.Op Fl S Ar src_addr +.Op Fl s Ar packetsize +.Op Fl t Ar timeout +.Op Fl W Ar waittime +.Op Fl z Ar tos +.Op Fl Fl apple-connect +.Op Fl Fl apple-time +.Ar host +.Nm +.Op Fl AaDdfLnoQqRrv +.Op Fl b Ar boundif +.Op Fl c Ar count +.Op Fl I Ar iface +.Op Fl i Ar wait +.Op Fl k Ar trafficclass +.Op Fl K Ar netservicetype +.Op Fl l Ar preload +.Op Fl M Cm mask | time +.Op Fl m Ar ttl +.Op Fl P Ar policy +.Op Fl p Ar pattern +.Op Fl S Ar src_addr +.Op Fl s Ar packetsize +.Op Fl T Ar ttl +.Op Fl t Ar timeout +.Op Fl W Ar waittime +.Op Fl z Ar tos +.Op Fl Fl apple-connect +.Op Fl Fl apple-time +.Ar mcast-group +.Sh DESCRIPTION +The +.Nm +utility uses the +.Tn ICMP +.No protocol Ap s mandatory +.Tn ECHO_REQUEST +datagram to elicit an +.Tn ICMP ECHO_RESPONSE +from a host or gateway. +.Tn ECHO_REQUEST +datagrams +.Pq Dq pings +have an IP and +.Tn ICMP +header, followed by a +.Dq struct timeval +and then an arbitrary number of +.Dq pad +bytes used to fill out the packet. +The options are as follows: +.Bl -tag -width indent +.It Fl A +Audible. +Output a bell +.Tn ( ASCII +0x07) +character when no packet is received before the next packet +is transmitted. +To cater for round-trip times that are longer than the interval +between transmissions, further missing packets cause a bell only +if the maximum number of unreceived packets has increased. +.It Fl a +Audible. +Include a bell +.Tn ( ASCII +0x07) +character in the output when any packet is received. +This option is ignored +if other format options are present. +.It Fl b Ar boundif +Bind the socket to interface +.Ar boundif +for sending. +This option is an Apple addition. +.It Fl C +Prohibit the socket from using the cellular network interface. +This option is an Apple addition. +.It Fl c Ar count +Stop after sending +(and receiving) +.Ar count +.Tn ECHO_RESPONSE +packets. +If this option is not specified, +.Nm +will operate until interrupted. +If this option is specified in conjunction with ping sweeps, +each sweep will consist of +.Ar count +packets. +.It Fl D +Set the Don't Fragment bit. +.It Fl d +Set the +.Dv SO_DEBUG +option on the socket being used. +.It Fl f +Flood ping. +Outputs packets as fast as they come back or one hundred times per second, +whichever is more. +For every +.Tn ECHO_REQUEST +sent a period +.Dq .\& +is printed, while for every +.Tn ECHO_REPLY +received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +Only the super-user may use this option. +.Bf -emphasis +This can be very hard on a network and should be used with caution. +.Ef +.It Fl G Ar sweepmaxsize +Specify the maximum size of +.Tn ICMP +payload when sending sweeping pings. +This option is required for ping sweeps. +.It Fl g Ar sweepminsize +Specify the size of +.Tn ICMP +payload to start with when sending sweeping pings. +The default value is 0. +.It Fl h Ar sweepincrsize +Specify the number of bytes to increment the size of +.Tn ICMP +payload after +each sweep when sending sweeping pings. +The default value is 1. +.It Fl I Ar iface +Source multicast packets with the given interface address. +This flag only applies if the ping destination is a multicast address. +.It Fl i Ar wait +Wait +.Ar wait +seconds +.Em between sending each packet . +The default is to wait for one second between each packet. +The wait time may be fractional, but only the super-user may specify +values less than 0.1 second. +This option is incompatible with the +.Fl f +option. +.It Fl k Ar trafficclass +Specifies the traffic class to use for sending ICMP packets. +The supported traffic classes are +BK_SYS, BK, BE, RD, OAM, AV, RV, VI, VO and CTL. +By default +.Nm +uses the control traffic class (CTL). +This option is an Apple addition. +.It Fl K Ar netservicetype +Specifies the network service type to use for sending ICMP packets. +The supported network service type are BK_SYS, BK, BE, RV, AV, RD, OAM, VI, SIG and VO. +Note this overrides the default traffic class (-k can still be specified after -K to use both). +This option is an Apple addition. +.It Fl L +Suppress loopback of multicast packets. +This flag only applies if the ping destination is a multicast address. +.It Fl l Ar preload +If +.Ar preload +is specified, +.Nm +sends that many packets as fast as possible before falling into its normal +mode of behavior. +Only the super-user may use this option. +.It Fl M Cm mask | time +Use +.Dv ICMP_MASKREQ +or +.Dv ICMP_TSTAMP +instead of +.Dv ICMP_ECHO . +For +.Cm mask , +print the netmask of the remote machine. +Set the +.Va net.inet.icmp.maskrepl +MIB variable to enable +.Dv ICMP_MASKREPLY . +For +.Cm time , +print the origination, reception and transmission timestamps. +.It Fl m Ar ttl +Set the IP Time To Live for outgoing packets. +If not specified, the kernel uses the value of the +.Va net.inet.ip.ttl +MIB variable. +.It Fl n +Numeric output only. +No attempt will be made to lookup symbolic names for host addresses. +.It Fl o +Exit successfully after receiving one reply packet. +.It Fl P Ar policy +.Ar policy +specifies IPsec policy for the ping session. +For details please refer to +.Xr ipsec 4 +and +.Xr ipsec_set_policy 3 . +.It Fl p Ar pattern +You may specify up to 16 +.Dq pad +bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, +.Dq Li \-p ff +will cause the sent packet to be filled with all +ones. +.It Fl Q +Somewhat quiet output. +.No Don Ap t +display ICMP error messages that are in response to our query messages. +Originally, the +.Fl v +flag was required to display such errors, but +.Fl v +displays all ICMP error messages. +On a busy machine, this output can be overbearing. +Without the +.Fl Q +flag, +.Nm +prints out any ICMP error messages caused by its own ECHO_REQUEST +messages. +.It Fl q +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. +.It Fl R +Record route. +Includes the +.Tn RECORD_ROUTE +option in the +.Tn ECHO_REQUEST +packet and displays +the route buffer on returned packets. +Note that the IP header is only large enough for nine such routes; +the +.Xr traceroute 8 +command is usually better at determining the route packets take to a +particular destination. +If more routes come back than should, such as due to an illegal spoofed +packet, ping will print the route list and then truncate it at the correct +spot. +Many hosts ignore or discard the +.Tn RECORD_ROUTE +option. +.It Fl r +Bypass the normal routing tables and send directly to a host on an attached +network. +If the host is not on a directly-attached network, an error is returned. +This option can be used to ping a local host through an interface +that has no route through it +(e.g., after the interface was dropped by +.Xr routed 8 ) . +.It Fl S Ar src_addr +Use the following IP address as the source address in outgoing packets. +On hosts with more than one IP address, this option can be used to +force the source address to be something other than the IP address +of the interface the probe packet is sent on. +If the IP address +is not one of this machine's interface addresses, an error is +returned and nothing is sent. +.It Fl s Ar packetsize +Specify the number of data bytes to be sent. +The default is 56, which translates into 64 +.Tn ICMP +data bytes when combined +with the 8 bytes of +.Tn ICMP +header data. +This option cannot be used with ping sweeps. +.It Fl T Ar ttl +Set the IP Time To Live for multicasted packets. +This flag only applies if the ping destination is a multicast address. +.It Fl t Ar timeout +Specify a timeout, in seconds, before ping exits regardless of how +many packets have been received. +.It Fl v +Verbose output. +.Tn ICMP +packets other than +.Tn ECHO_RESPONSE +that are received are listed. +.It Fl W Ar waittime +Time in milliseconds to wait for a reply for each packet sent. +If a reply arrives later, the packet is not printed as replied, but +considered as replied when calculating statistics. +.It Fl z Ar tos +Use the specified type of service. +.It Fl Fl apple-connect +Connects the socket to the destination address. +This option is an Apple addition. +.It Fl Fl apple-time +Prints the time a packet was received. +This option is an Apple addition. +.El +.Pp +When using +.Nm +for fault isolation, it should first be run on the local host, to verify +that the local network interface is up and running. +Then, hosts and gateways further and further away should be +.Dq pinged . +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the round-trip time statistics. +When the specified number of packets have been sent +(and received) +or if the program is terminated with a +.Dv SIGINT , +a brief summary is displayed, showing the number of packets sent and +received, and the minimum, mean, maximum, and standard deviation of +the round-trip times. +.Pp +If +.Nm +receives a +.Dv SIGINFO +(see the +.Cm status +argument for +.Xr stty 1 ) +signal, the current number of packets sent and received, and the +minimum, mean, and maximum of the round-trip times will be written to +the standard error output. +.Pp +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +.Nm +during normal operations or from automated scripts. +.Sh ICMP PACKET DETAILS +An IP header without options is 20 bytes. +An +.Tn ICMP +.Tn ECHO_REQUEST +packet contains an additional 8 bytes worth of +.Tn ICMP +header followed by an arbitrary amount of data. +When a +.Ar packetsize +is given, this indicated the size of this extra piece of data +(the default is 56). +Thus the amount of data received inside of an IP packet of type +.Tn ICMP +.Tn ECHO_REPLY +will always be 8 bytes more than the requested data space +(the +.Tn ICMP +header). +.Pp +If the data space is at least eight bytes large, +.Nm +uses the first eight bytes of this space to include a timestamp which +it uses in the computation of round trip times. +If less than eight bytes of pad are specified, no round trip times are +given. +.Sh DUPLICATE AND DAMAGED PACKETS +The +.Nm +utility will report duplicate and damaged packets. +Duplicate packets should never occur when pinging a unicast address, +and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely +(if ever) +a good sign, although the presence of low levels of duplicates may not +always be cause for alarm. +Duplicates are expected when pinging a broadcast or multicast address, +since they are not really duplicates but replies from different hosts +to the same request. +.Pp +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +.Nm +packet's path (in the network or in the hosts). +.Sh TRYING DIFFERENT DATA PATTERNS +The +(inter)network +layer should never treat packets differently depending on the data +contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that does not have sufficient +.Dq transitions , +such as all ones or all zeros, or a pattern right at the edge, such as +almost all zeros. +It is not +necessarily enough to specify a data pattern of all zeros (for example) +on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. +.Pp +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either +cannot +be sent across your network or that takes much longer to transfer than +other similar length files. +You can then examine this file for repeated patterns that you can test +using the +.Fl p +option of +.Nm . +.Sh TTL DETAILS +The +.Tn TTL +value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement +the +.Tn TTL +field by exactly one. +.Pp +The +.Tn TCP/IP +specification recommends setting the +.Tn TTL +field for +.Tn IP +packets to 64, but many systems use smaller values +.No ( Bx 4.3 +uses 30, +.Bx 4.2 +used 15). +.Pp +The maximum possible value of this field is 255, and most +.Ux +systems set +the +.Tn TTL +field of +.Tn ICMP ECHO_REQUEST +packets to 255. +This is why you will find you can +.Dq ping +some hosts, but not reach them with +.Xr telnet 1 +or +.Xr ftp 1 . +.Pp +In normal operation +.Nm +prints the ttl value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the +.Tn TTL +field in its response: +.Bl -bullet +.It +Not change it; this is what +.Bx +systems did before the +.Bx 4.3 tahoe +release. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the round-trip path. +.It +Set it to 255; this is what current +.Bx +systems do. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the path +.Em from +the remote system +.Em to +the +.Nm Ns Em ing +host. +.It +Set it to some other value. +Some machines use the same value for +.Tn ICMP +packets that they use for +.Tn TCP +packets, for example either 30 or 60. +Others may use completely wild values. +.El +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Bl -tag -width indent +.It 0 +At least one response was heard from the specified +.Ar host . +.It 2 +The transmission was successful but no responses were received. +.It any other value +An error occurred. +These values are defined in +.In sysexits.h . +.El +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ifconfig 8 , +.Xr routed 8 , +.Xr traceroute 8 , +.Xr ping6 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.3 . +.Sh AUTHORS +The original +.Nm +utility was written by +.An Mike Muuss +while at the US Army Ballistics +Research Laboratory. +.Sh BUGS +Many Hosts and Gateways ignore the +.Tn RECORD_ROUTE +option. +.Pp +The maximum IP header length is too small for options like +.Tn RECORD_ROUTE +to be completely useful. +.No There Ap s +not much that can be done about this, however. +.Pp +Flood pinging is not recommended in general, and flood pinging the +broadcast address should only be done under very controlled conditions. +.Pp +The +.Fl v +option is not worth much on busy hosts. diff --git a/network_cmds/ping.tproj/ping.c b/network_cmds/ping.tproj/ping.c new file mode 100644 index 0000000..58e7f80 --- /dev/null +++ b/network_cmds/ping.tproj/ping.c @@ -0,0 +1,2134 @@ +/* + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* + * P I N G . C + * + * Using the Internet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include <sys/param.h> /* NB: we rely on this for <sys/types.h> */ +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/ip_var.h> +#include <arpa/inet.h> +#include <net/if.h> + +#ifdef IPSEC +#include <netinet6/ipsec.h> +#endif /*IPSEC*/ + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <math.h> +#include <netdb.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <ifaddrs.h> +#include <getopt.h> + +#define INADDR_LEN ((int)sizeof(in_addr_t)) +#define TIMEVAL_LEN ((int)sizeof(struct tv32)) +#define MASK_LEN (ICMP_MASKLEN - ICMP_MINLEN) +#define TS_LEN (ICMP_TSLEN - ICMP_MINLEN) +#define DEFDATALEN 56 /* default data length */ +#define FLOOD_BACKOFF 20000 /* usecs to back off if F_FLOOD mode */ + /* runs out of buffer space */ +#define MAXIPLEN (sizeof(struct ip) + MAX_IPOPTLEN) +#define MAXICMPLEN (ICMP_ADVLENMIN + MAX_IPOPTLEN) +#define MAXWAIT 10000 /* max ms to wait for response */ +#define MAXALARM (60 * 60) /* max seconds for alarm timeout */ +#define MAXTOS 255 + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +struct tv32 { + u_int32_t tv32_sec; + u_int32_t tv32_usec; +}; + +/* various options */ +int options; +#define F_FLOOD 0x0001 +#define F_INTERVAL 0x0002 +#define F_NUMERIC 0x0004 +#define F_PINGFILLED 0x0008 +#define F_QUIET 0x0010 +#define F_RROUTE 0x0020 +#define F_SO_DEBUG 0x0040 +#define F_SO_DONTROUTE 0x0080 +#define F_VERBOSE 0x0100 +#define F_QUIET2 0x0200 +#define F_NOLOOP 0x0400 +#define F_MTTL 0x0800 +#define F_MIF 0x1000 +#define F_AUDIBLE 0x2000 +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +#define F_POLICY 0x4000 +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ +#define F_TTL 0x8000 +#define F_MISSED 0x10000 +#define F_ONCE 0x20000 +#define F_HDRINCL 0x40000 +#define F_MASK 0x80000 +#define F_TIME 0x100000 +#define F_SWEEP 0x200000 +#define F_WAITTIME 0x400000 +#define F_CONNECT 0x800000 +#define F_PRTIME 0x1000000 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 128) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct sockaddr_in whereto; /* who to ping */ +int datalen = DEFDATALEN; +int maxpayload; +int s; /* socket file descriptor */ +u_char outpackhdr[IP_MAXPACKET], *outpack; +char BBELL = '\a'; /* characters written for MISSED and AUDIBLE */ +char BSPACE = '\b'; /* characters written for flood */ +char DOT = '.'; +char *hostname; +char *shostname; +int ident; /* process id to identify our packets */ +int uid; /* cached uid for micro-optimization */ +u_char icmp_type = ICMP_ECHO; +u_char icmp_type_rsp = ICMP_ECHOREPLY; +int phdr_len = 0; +int send_len; +char *boundif; +unsigned int ifscope; +int nocell; +int use_sendmsg = 0; +int use_recvmsg = 0; +int traffic_class = SO_TC_CTL; /* use control class, by default */ +int net_service_type = -1; +int no_dup = 0; + +/* counters */ +long nmissedmax; /* max value of ntransmitted - nreceived - 1 */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +long snpackets; /* max packets to transmit in one sweep */ +long snreceived; /* # of packets we got back in this sweep */ +long sntransmitted; /* # of packets we sent in this sweep */ +int sweepmax; /* max value of payload in sweep */ +int sweepmin = 0; /* start value of payload in sweep */ +int sweepincr = 1; /* payload increment in sweep */ +int interval = 1000; /* interval between packets, ms */ +int waittime = MAXWAIT; /* timeout for each packet */ +long nrcvtimeout = 0; /* # of packets we got back after waittime */ +int icmp_len = 0; /* length of the ICMP header */ + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ +double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ + +volatile sig_atomic_t finish_up; /* nonzero if we've been told to finish up */ +volatile sig_atomic_t siginfo_p; + +static void fill(char *, char *); +static u_short in_cksum(u_short *, int); +static void check_status(void); +static void finish(void) __dead2; +static void pinger(void); +static char *pr_addr(struct in_addr); +static char *pr_ntime(n_time); +static void pr_icmph(struct icmp *); +static void pr_iph(struct ip *); +static void pr_pack(char *, int, struct sockaddr_in *, struct timeval *, int); +static void pr_retip(struct ip *); +static void status(int); +static void stopit(int); +static void tvsub(struct timeval *, const struct timeval *); +static int str2sotc(const char *, bool *); +static int str2netservicetype(const char *, bool *); +static u_int8_t str2tos(const char *, bool *); +static void usage(void) __dead2; + +int32_t thiszone; /* seconds offset from gmt to local time */ +extern int32_t gmt2local(time_t); +static void pr_currenttime(void); + +static int longopt_flag = 0; + +#define LOF_CONNECT 0x01 +#define LOF_PRTIME 0x02 + +static const struct option longopts[] = { + { "apple-connect", no_argument, &longopt_flag, LOF_CONNECT }, + { "apple-time", no_argument, &longopt_flag, LOF_PRTIME }, + { NULL, 0, NULL, 0 } +}; + +int +main(int argc, char *const *argv) +{ + struct sockaddr_in from, sock_in; + struct in_addr ifaddr; + struct timeval last, intvl; + struct iovec iov; + struct ip *ip; + struct msghdr msg; + struct sigaction si_sa; + size_t sz; + u_char *datap, packet[IP_MAXPACKET] __attribute__((aligned(4))); + char *ep, *source, *target, *payload; + struct hostent *hp; +#ifdef IPSEC_POLICY_IPSEC + char *policy_in, *policy_out; +#endif + struct sockaddr_in *to; + double t; + u_long alarmtimeout, ultmp; + int almost_done, ch, df, hold, i, mib[4], preload, sockerrno, + tos, ttl; + char ctrl[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(int))]; + char hnamebuf[MAXHOSTNAMELEN], snamebuf[MAXHOSTNAMELEN]; +#ifdef IP_OPTIONS + char rspace[MAX_IPOPTLEN]; /* record route space */ +#endif + unsigned char loop, mttl; + + payload = source = NULL; +#ifdef IPSEC_POLICY_IPSEC + policy_in = policy_out = NULL; +#endif + bool valid; + + /* + * Do the stuff that we need root priv's for *first*, and + * then drop our setuid bit. Save error reporting for + * after arg parsing. + */ + if (getuid()) + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + else + s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + sockerrno = errno; + + if (setuid(getuid()) != 0) + err(EX_NOPERM, "setuid() failed"); + uid = getuid(); + + alarmtimeout = df = preload = tos = 0; + + outpack = outpackhdr + sizeof(struct ip); + while ((ch = getopt_long(argc, argv, + "AaB:b:Cc:DdfG:g:h:I:i:k:K:Ll:M:m:noP:p:QqRrS:s:T:t:vW:z:", + longopts, NULL)) != -1) + { + switch(ch) { + case 'A': + options |= F_MISSED; + break; + case 'a': + options |= F_AUDIBLE; + break; + case 'B': + case 'b': + boundif = optarg; + break; + case 'C': + nocell++; + break; + case 'c': + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > LONG_MAX || !ultmp) + errx(EX_USAGE, + "invalid count of packets to transmit: `%s'", + optarg); + npackets = ultmp; + break; + case 'D': + options |= F_HDRINCL; + df = 1; + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'f': + if (uid) { + errno = EPERM; + err(EX_NOPERM, "-f flag"); + } + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + break; + case 'G': /* Maximum packet size for ping sweep */ + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg) + errx(EX_USAGE, "invalid packet size: `%s'", + optarg); +#ifndef __APPLE__ + if (uid != 0 && ultmp > DEFDATALEN) { + errno = EPERM; + err(EX_NOPERM, + "packet size too large: %lu > %u", + ultmp, DEFDATALEN); + } +#endif /* __APPLE__ */ + options |= F_SWEEP; + sweepmax = ultmp; + break; + case 'g': /* Minimum packet size for ping sweep */ + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg) + errx(EX_USAGE, "invalid packet size: `%s'", + optarg); +#ifndef __APPLE__ + if (uid != 0 && ultmp > DEFDATALEN) { + errno = EPERM; + err(EX_NOPERM, + "packet size too large: %lu > %u", + ultmp, DEFDATALEN); + } +#endif /* __APPLE__ */ + options |= F_SWEEP; + sweepmin = ultmp; + break; + case 'h': /* Packet size increment for ping sweep */ + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp < 1) + errx(EX_USAGE, "invalid increment size: `%s'", + optarg); +#ifndef __APPLE__ + if (uid != 0 && ultmp > DEFDATALEN) { + errno = EPERM; + err(EX_NOPERM, + "packet size too large: %lu > %u", + ultmp, DEFDATALEN); + } +#endif /* __APPLE__ */ + options |= F_SWEEP; + sweepincr = ultmp; + break; + case 'I': /* multicast interface */ + if (inet_aton(optarg, &ifaddr) == 0) + errx(EX_USAGE, + "invalid multicast interface: `%s'", + optarg); + options |= F_MIF; + break; + case 'i': /* wait between sending packets */ + t = strtod(optarg, &ep) * 1000.0; + if (*ep || ep == optarg || t > (double)INT_MAX) + errx(EX_USAGE, "invalid timing interval: `%s'", + optarg); + options |= F_INTERVAL; + interval = (int)t; + if (uid && interval < 100) { + errno = EPERM; + err(EX_NOPERM, "-i interval too short"); + } + break; + case 'k': + if (strcasecmp(optarg, "sendmsg") == 0) { + use_sendmsg++; + break; + } + if (strcasecmp(optarg, "recvmsg") == 0) { + use_recvmsg++; + break; + } + traffic_class = str2sotc(optarg, &valid); + if (valid == false) + errx(EX_USAGE, "bad traffic class: `%s'", + optarg); + break; + case 'K': + if (strcasecmp(optarg, "sendmsg") == 0) { + use_sendmsg++; + break; + } + net_service_type = str2netservicetype(optarg, &valid); + if (valid == false) + errx(EX_USAGE, "bad network service type: `%s'", + optarg); + /* suppress default traffic class (-k can still be specified after -K) */ + traffic_class = -1; + break; + case 'L': + options |= F_NOLOOP; + loop = 0; + break; + case 'l': + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > INT_MAX) + errx(EX_USAGE, + "invalid preload value: `%s'", optarg); + if (uid) { + errno = EPERM; + err(EX_NOPERM, "-l flag"); + } + preload = ultmp; + break; + case 'M': + switch(optarg[0]) { + case 'M': + case 'm': + options |= F_MASK; + break; + case 'T': + case 't': + options |= F_TIME; + break; + default: + errx(EX_USAGE, "invalid message: `%c'", optarg[0]); + break; + } + break; + case 'm': /* TTL */ + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > MAXTTL) + errx(EX_USAGE, "invalid TTL: `%s'", optarg); + ttl = ultmp; + options |= F_TTL; + break; + case 'n': + options |= F_NUMERIC; + break; + case 'o': + options |= F_ONCE; + break; + case 'P': +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + options |= F_POLICY; + if (!strncmp("in", optarg, 2)) + policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + policy_out = strdup(optarg); + else + errx(1, "invalid security policy"); +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + payload = optarg; + break; + case 'Q': + options |= F_QUIET2; + break; + case 'q': + options |= F_QUIET; + break; + case 'R': + options |= F_RROUTE; + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 'S': + source = optarg; + break; + case 's': /* size of packet to send */ + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg) + errx(EX_USAGE, "invalid packet size: `%s'", + optarg); +#ifndef __APPLE__ + if (uid != 0 && ultmp > DEFDATALEN) { + errno = EPERM; + err(EX_NOPERM, + "packet size too large: %lu > %u", + ultmp, DEFDATALEN); + } +#endif /* __APPLE__ */ + datalen = ultmp; + break; + case 'T': /* multicast TTL */ + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > MAXTTL) + errx(EX_USAGE, "invalid multicast TTL: `%s'", + optarg); + mttl = ultmp; + options |= F_MTTL; + break; + case 't': + alarmtimeout = strtoul(optarg, &ep, 0); + if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) + errx(EX_USAGE, "invalid timeout: `%s'", + optarg); + if (alarmtimeout > MAXALARM) + errx(EX_USAGE, "invalid timeout: `%s' > %d", + optarg, MAXALARM); + alarm((unsigned int)alarmtimeout); + break; + case 'v': + options |= F_VERBOSE; + break; + case 'W': /* wait ms for answer */ + t = strtod(optarg, &ep); + if (*ep || ep == optarg || t > (double)INT_MAX) + errx(EX_USAGE, "invalid timing interval: `%s'", + optarg); + options |= F_WAITTIME; + waittime = (int)t; + break; + case 'z': + options |= F_HDRINCL; + tos = str2tos(optarg, &valid); + if (valid == false) + errx(EX_USAGE, "invalid TOS: `%s'", optarg); + break; + case 0: + switch (longopt_flag) { + case LOF_CONNECT: + options |= F_CONNECT; + break; + case LOF_PRTIME: + options |= F_PRTIME; + thiszone = gmt2local(0); + break; + default: + break; + } + longopt_flag = 0; + break; + default: + usage(); + } + } + + if (boundif != NULL && (ifscope = if_nametoindex(boundif)) == 0) + errx(1, "bad interface name"); + + if (argc - optind != 1) + usage(); + target = argv[optind]; + + switch (options & (F_MASK|F_TIME)) { + case 0: break; + case F_MASK: + icmp_type = ICMP_MASKREQ; + icmp_type_rsp = ICMP_MASKREPLY; + phdr_len = MASK_LEN; + if (!(options & F_QUIET)) + (void)printf("ICMP_MASKREQ\n"); + break; + case F_TIME: + icmp_type = ICMP_TSTAMP; + icmp_type_rsp = ICMP_TSTAMPREPLY; + phdr_len = TS_LEN; + if (!(options & F_QUIET)) + (void)printf("ICMP_TSTAMP\n"); + break; + default: + errx(EX_USAGE, "ICMP_TSTAMP and ICMP_MASKREQ are exclusive."); + break; + } + icmp_len = sizeof(struct ip) + ICMP_MINLEN + phdr_len; + if (options & F_RROUTE) + icmp_len += MAX_IPOPTLEN; + maxpayload = IP_MAXPACKET - icmp_len; + if (datalen > maxpayload) + errx(EX_USAGE, "packet size too large: %d > %d", datalen, + maxpayload); + send_len = icmp_len + datalen; + datap = &outpack[ICMP_MINLEN + phdr_len + TIMEVAL_LEN]; + if (options & F_PINGFILLED) { + fill((char *)datap, payload); + } + if (source) { + bzero((char *)&sock_in, sizeof(sock_in)); + sock_in.sin_family = AF_INET; + if (inet_aton(source, &sock_in.sin_addr) != 0) { + shostname = source; + } else { + hp = gethostbyname2(source, AF_INET); + if (!hp) + errx(EX_NOHOST, "cannot resolve %s: %s", + source, hstrerror(h_errno)); + + sock_in.sin_len = sizeof sock_in; + if ((unsigned)hp->h_length > sizeof(sock_in.sin_addr) || + hp->h_length < 0) + errx(1, "gethostbyname2: illegal address"); + memcpy(&sock_in.sin_addr, hp->h_addr_list[0], + sizeof(sock_in.sin_addr)); + (void)strlcpy(snamebuf, hp->h_name, + sizeof(snamebuf)); + shostname = snamebuf; + } + if (bind(s, (struct sockaddr *)&sock_in, sizeof sock_in) == -1) +#if (DEBUG || DEVELOPMENT) + options |= F_HDRINCL; +#else + err(1, "bind"); +#endif /* DEBUG || DEVELOPMENT */ + } + + bzero(&whereto, sizeof(whereto)); + to = &whereto; + to->sin_family = AF_INET; + to->sin_len = sizeof *to; + if (inet_aton(target, &to->sin_addr) != 0) { + hostname = target; + } else { + hp = gethostbyname2(target, AF_INET); + if (!hp) + errx(EX_NOHOST, "cannot resolve %s: %s", + target, hstrerror(h_errno)); + + if ((unsigned)hp->h_length > sizeof(to->sin_addr)) + errx(1, "gethostbyname2 returned an illegal address"); + memcpy(&to->sin_addr, hp->h_addr_list[0], sizeof to->sin_addr); + (void)strlcpy(hnamebuf, hp->h_name, sizeof(hnamebuf)); + hostname = hnamebuf; + } + + do { + struct ifaddrs *ifa_list, *ifa; + + if (IN_MULTICAST(ntohl(whereto.sin_addr.s_addr)) || whereto.sin_addr.s_addr == INADDR_BROADCAST) { + no_dup = 1; + break; + } + + if (getifaddrs(&ifa_list) == -1) + break; + for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + if ((ifa->ifa_flags & IFF_BROADCAST) == 0 || ifa->ifa_broadaddr == NULL) + continue; + if (whereto.sin_addr.s_addr != ((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr) + continue; + no_dup = 1; + break; + } + + freeifaddrs(ifa_list); + } while (0); + + if (options & F_FLOOD && options & F_INTERVAL) + errx(EX_USAGE, "-f and -i: incompatible options"); + + if (options & F_FLOOD && IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + errx(EX_USAGE, + "-f flag cannot be used with multicast destination"); + if (options & (F_MIF | F_NOLOOP | F_MTTL) + && !IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + errx(EX_USAGE, + "-I, -L, -T flags cannot be used with unicast destination"); + + if (!(options & F_PINGFILLED)) + for (i = TIMEVAL_LEN; i < MAX(datalen, sweepmax); ++i) + *datap++ = i; + + ident = getpid() & 0xFFFF; + + if (s < 0) { + errno = sockerrno; + err(EX_OSERR, "socket"); + } + hold = 1; + (void) setsockopt(s, SOL_SOCKET, SO_RECV_ANYIF, (char *)&hold, + sizeof(hold)); + if (ifscope != 0) { + if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF, + (char *)&ifscope, sizeof (ifscope)) != 0) + err(EX_OSERR, "setsockopt(IP_BOUND_IF)"); + } + if (nocell) { + if (setsockopt(s, IPPROTO_IP, IP_NO_IFT_CELLULAR, + (char *)&nocell, sizeof (nocell)) != 0) + err(EX_OSERR, "setsockopt(IP_NO_IFT_CELLULAR)"); + } + if (options & F_SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, + sizeof(hold)); + if (options & F_SO_DONTROUTE) + (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, + sizeof(hold)); + if (use_sendmsg == 0) { + if (net_service_type != -1) + if (setsockopt(s, SOL_SOCKET, SO_NET_SERVICE_TYPE, + (void *)&net_service_type, sizeof (net_service_type)) != 0) + warn("setsockopt(SO_NET_SERVICE_TYPE"); + if (traffic_class != -1) { + if (setsockopt(s, SOL_SOCKET, SO_TRAFFIC_CLASS, + (void *)&traffic_class, sizeof (traffic_class)) != 0) + warn("setsockopt(SO_TRAFFIC_CLASS"); + + } + } + if (use_recvmsg > 0) { + int on = 1; + (void) setsockopt(s, SOL_SOCKET, SO_RECV_TRAFFIC_CLASS, + (void *)&on, sizeof (on)); + } +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + if (options & F_POLICY) { + char *buf; + if (policy_in != NULL) { + buf = ipsec_set_policy(policy_in, strlen(policy_in)); + if (buf == NULL) + errx(EX_CONFIG, "%s", ipsec_strerror()); + if (setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)) < 0) + err(EX_CONFIG, + "ipsec policy cannot be configured"); + free(buf); + } + + if (policy_out != NULL) { + buf = ipsec_set_policy(policy_out, strlen(policy_out)); + if (buf == NULL) + errx(EX_CONFIG, "%s", ipsec_strerror()); + if (setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)) < 0) + err(EX_CONFIG, + "ipsec policy cannot be configured"); + free(buf); + } + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + + if (options & F_HDRINCL) { + ip = (struct ip*)outpackhdr; + if (!(options & (F_TTL | F_MTTL))) { + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_DEFTTL; + sz = sizeof(ttl); + if (sysctl(mib, 4, &ttl, &sz, NULL, 0) == -1) + err(1, "sysctl(net.inet.ip.ttl)"); + } + setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hold, sizeof(hold)); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_tos = tos; + ip->ip_id = 0; + ip->ip_off = df ? IP_DF : 0; + ip->ip_ttl = ttl; + ip->ip_p = IPPROTO_ICMP; + ip->ip_src.s_addr = source ? sock_in.sin_addr.s_addr : INADDR_ANY; + ip->ip_dst = to->sin_addr; + } + /* record route option */ + if (options & F_RROUTE) { +#ifdef IP_OPTIONS + bzero(rspace, sizeof(rspace)); + rspace[IPOPT_OPTVAL] = IPOPT_RR; + rspace[IPOPT_OLEN] = sizeof(rspace) - 1; + rspace[IPOPT_OFFSET] = IPOPT_MINOFF; + rspace[sizeof(rspace) - 1] = IPOPT_EOL; + if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, + sizeof(rspace)) < 0) + err(EX_OSERR, "setsockopt IP_OPTIONS"); +#else + errx(EX_UNAVAILABLE, + "record route not available in this implementation"); +#endif /* IP_OPTIONS */ + } + + if (options & F_TTL) { + if (setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, + sizeof(ttl)) < 0) { + err(EX_OSERR, "setsockopt IP_TTL"); + } + } + if (options & F_NOLOOP) { + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)) < 0) { + err(EX_OSERR, "setsockopt IP_MULTICAST_LOOP"); + } + } + if (options & F_MTTL) { + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, + sizeof(mttl)) < 0) { + err(EX_OSERR, "setsockopt IP_MULTICAST_TTL"); + } + } + if (options & F_MIF) { + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, + sizeof(ifaddr)) < 0) { + err(EX_OSERR, "setsockopt IP_MULTICAST_IF"); + } + } +#ifdef SO_TIMESTAMP + { int on = 1; + if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0) + err(EX_OSERR, "setsockopt SO_TIMESTAMP"); + } +#endif + + if ((options & F_CONNECT)) { + if (connect(s, (struct sockaddr *)&whereto, sizeof whereto) == -1) + err(EX_OSERR, "connect"); + } + + if (sweepmax) { + if (sweepmin >= sweepmax) + errx(EX_USAGE, "Maximum packet size must be greater than the minimum packet size"); + + if (datalen != DEFDATALEN) + errx(EX_USAGE, "Packet size and ping sweep are mutually exclusive"); + + if (npackets > 0) { + snpackets = npackets; + npackets = 0; + } else + snpackets = 1; + datalen = sweepmin; + send_len = icmp_len + sweepmin; + } + if (options & F_SWEEP && !sweepmax) + errx(EX_USAGE, "Maximum sweep size must be specified"); + + /* + * When pinging the broadcast address, you can get a lot of answers. + * Doing something so evil is useful if you are trying to stress the + * ethernet, or just want to fill the arp cache to get some stuff for + * /etc/ethers. But beware: RFC 1122 allows hosts to ignore broadcast + * or multicast pings if they wish. + */ + + /* + * XXX receive buffer needs undetermined space for mbuf overhead + * as well. + */ + hold = IP_MAXPACKET + 128; + (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, + sizeof(hold)); + if (uid == 0) + (void)setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&hold, + sizeof(hold)); + + if (to->sin_family == AF_INET) { + (void)printf("PING %s (%s)", hostname, + inet_ntoa(to->sin_addr)); + if (source) + (void)printf(" from %s", shostname); + if (sweepmax) + (void)printf(": (%d ... %d) data bytes\n", + sweepmin, sweepmax); + else + (void)printf(": %d data bytes\n", datalen); + + } else { + if (sweepmax) + (void)printf("PING %s: (%d ... %d) data bytes\n", + hostname, sweepmin, sweepmax); + else + (void)printf("PING %s: %d data bytes\n", hostname, datalen); + } + + /* + * rdar://25829310 + * + * Clear blocked signals inherited from the parent + */ + sigset_t newset; + sigemptyset(&newset); + if (sigprocmask(SIG_SETMASK, &newset, NULL) != 0) + err(EX_OSERR, "sigprocmask(newset)"); + + /* + * Use sigaction() instead of signal() to get unambiguous semantics, + * in particular with SA_RESTART not set. + */ + + sigemptyset(&si_sa.sa_mask); + si_sa.sa_flags = 0; + + si_sa.sa_handler = stopit; + if (sigaction(SIGINT, &si_sa, 0) == -1) { + err(EX_OSERR, "sigaction SIGINT"); + } + si_sa.sa_handler = stopit; + if (sigaction(SIGQUIT, &si_sa, 0) == -1) { + err(EX_OSERR, "sigaction SIGQUIT"); + } + + si_sa.sa_handler = status; + if (sigaction(SIGINFO, &si_sa, 0) == -1) { + err(EX_OSERR, "sigaction SIGINFO"); + } + + if (alarmtimeout > 0) { + si_sa.sa_handler = stopit; + if (sigaction(SIGALRM, &si_sa, 0) == -1) + err(EX_OSERR, "sigaction SIGALRM"); + } + + bzero(&msg, sizeof(msg)); + msg.msg_name = (caddr_t)&from; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; +#ifdef SO_TIMESTAMP + msg.msg_control = (caddr_t)ctrl; +#endif + iov.iov_base = packet; + iov.iov_len = IP_MAXPACKET; + + if (preload == 0) + pinger(); /* send the first ping */ + else { + if (npackets != 0 && preload > npackets) + preload = npackets; + while (preload--) /* fire off them quickies */ + pinger(); + } + (void)gettimeofday(&last, NULL); + + if (options & F_FLOOD) { + intvl.tv_sec = 0; + intvl.tv_usec = 10000; + } else { + intvl.tv_sec = interval / 1000; + intvl.tv_usec = interval % 1000 * 1000; + } + + almost_done = 0; + while (!finish_up) { + struct timeval now, timeout; + fd_set rfds; + int cc, n; + int tc = -1; + + check_status(); + if ((unsigned)s >= FD_SETSIZE) + errx(EX_OSERR, "descriptor too large"); + FD_ZERO(&rfds); + FD_SET(s, &rfds); + (void)gettimeofday(&now, NULL); + timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec; + timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec; + while (timeout.tv_usec < 0) { + timeout.tv_usec += 1000000; + timeout.tv_sec--; + } + while (timeout.tv_usec >= 1000000) { + timeout.tv_usec -= 1000000; + timeout.tv_sec++; + } + if (timeout.tv_sec < 0) + timeout.tv_sec = timeout.tv_usec = 0; + n = select(s + 1, &rfds, NULL, NULL, &timeout); + if (n < 0) + continue; /* Must be EINTR. */ + if (n == 1) { + struct timeval *tv = NULL; +#ifdef SO_TIMESTAMP + struct cmsghdr *cmsg; + + msg.msg_controllen = sizeof(ctrl); +#endif + msg.msg_namelen = sizeof(from); + if ((cc = recvmsg(s, &msg, 0)) < 0) { + if (errno == EINTR) + continue; + warn("recvmsg"); + continue; + } + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { +#ifdef SO_TIMESTAMP + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof *tv)) { + /* Copy to avoid alignment problems: */ + memcpy(&now, CMSG_DATA(cmsg), sizeof(now)); + tv = &now; + } +#endif + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TRAFFIC_CLASS && + cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + /* Copy to avoid alignment problems: */ + memcpy(&tc, CMSG_DATA(cmsg), sizeof(tc)); + } + } + if (tv == NULL) { + (void)gettimeofday(&now, NULL); + tv = &now; + } + pr_pack((char *)packet, cc, &from, tv, tc); + if ((options & F_ONCE && nreceived) || + (npackets && nreceived >= npackets)) + break; + } + if (n == 0 || options & F_FLOOD) { + if (sweepmax && sntransmitted == snpackets) { + datalen += sweepincr; + if (datalen > sweepmax) + break; + send_len = icmp_len + datalen; + sntransmitted = 0; + } + if (!npackets || ntransmitted < npackets) + pinger(); + else { + if (almost_done) + break; + almost_done = 1; + intvl.tv_usec = 0; + if (nreceived) { + intvl.tv_sec = 2 * tmax / 1000; + if (!intvl.tv_sec) + intvl.tv_sec = 1; + } else { + intvl.tv_sec = waittime / 1000; + intvl.tv_usec = waittime % 1000 * 1000; + } + } + (void)gettimeofday(&last, NULL); + if (ntransmitted - nreceived - 1 > nmissedmax) { + nmissedmax = ntransmitted - nreceived - 1; + if (options & F_MISSED) + (void)write(STDOUT_FILENO, &BBELL, 1); + if (!(options & F_QUIET)) { + printf("Request timeout for icmp_seq %u\n", + (uint16_t)(ntransmitted - 2)); + if (!(options & F_FLOOD)) + (void)fflush(stdout); + } + } + } + } + finish(); + /* NOTREACHED */ + exit(0); /* Make the compiler happy */ +} + +/* + * stopit -- + * Set the global bit that causes the main loop to quit. + * Do NOT call finish() from here, since finish() does far too much + * to be called from a signal handler. + */ +void +stopit(int sig __unused) +{ + + /* + * When doing reverse DNS lookups, the finish_up flag might not + * be noticed for a while. Just exit if we get a second SIGINT. + */ + if (!(options & F_NUMERIC) && finish_up) + _exit(nreceived ? 0 : 2); + finish_up = 1; +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first TIMEVAL_LEN + * bytes of the data portion are used to hold a UNIX "timeval" struct in + * host byte-order, to compute the round-trip time. + */ +static void +pinger(void) +{ + struct timeval now; + struct tv32 tv32; + struct ip *ip; + struct icmp *icp; + int cc, i; + u_char *packet; + + packet = outpack; + icp = (struct icmp *)outpack; + icp->icmp_type = icmp_type; + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_seq = htons(ntransmitted); + icp->icmp_id = ident; /* ID */ + + CLR(ntransmitted % mx_dup_ck); + + if (datalen >= TIMEVAL_LEN) /* can we time transfer */ + timing = 1; + else + timing = 0; + + if ((options & F_TIME) || timing) { + (void)gettimeofday(&now, NULL); + + tv32.tv32_sec = htonl(now.tv_sec); + tv32.tv32_usec = htonl(now.tv_usec); + if (options & F_TIME) + icp->icmp_otime = htonl((now.tv_sec % (24*60*60)) + * 1000 + now.tv_usec / 1000); + if (timing) + bcopy((void *)&tv32, + (void *)&outpack[ICMP_MINLEN + phdr_len], + sizeof(tv32)); + } + + cc = ICMP_MINLEN + phdr_len + datalen; + + /* compute ICMP checksum here */ + icp->icmp_cksum = in_cksum((u_short *)icp, cc); + + if (options & F_HDRINCL) { + cc += sizeof(struct ip); + ip = (struct ip *)outpackhdr; + ip->ip_len = cc; + ip->ip_sum = in_cksum((u_short *)outpackhdr, cc); + packet = outpackhdr; + } + if (use_sendmsg > 0) { + struct msghdr msg; + struct iovec iov; + char cmbuf[2 * CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cm = (struct cmsghdr *)cmbuf; + + if ((options & F_CONNECT)) { + msg.msg_name = NULL; + msg.msg_namelen = 0; + } else { + msg.msg_name = &whereto; + msg.msg_namelen = sizeof(whereto); + } + iov.iov_base = packet; + iov.iov_len = cc; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_controllen = 0; + msg.msg_control = NULL; + + if (traffic_class >= 0) { + cm->cmsg_len = CMSG_LEN(sizeof(int)); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SO_TRAFFIC_CLASS; + *(int *)CMSG_DATA(cm) = traffic_class; + msg.msg_controllen += CMSG_SPACE(sizeof(int)); + cm = (struct cmsghdr *)(((char *)cm) + CMSG_SPACE(sizeof(int))); + } + if (net_service_type >= 0) { + cm->cmsg_len = CMSG_LEN(sizeof(int)); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SO_NET_SERVICE_TYPE; + msg.msg_controllen += CMSG_SPACE(sizeof(int)); + *(int *)CMSG_DATA(cm) = net_service_type; + } + msg.msg_control = cmbuf; + + msg.msg_flags = 0; + + i = sendmsg(s, &msg, 0); + } else { + if ((options & F_CONNECT)) { + i = send(s, (char *)packet, cc, 0); + } else { + i = sendto(s, (char *)packet, cc, 0, (struct sockaddr *)&whereto, + sizeof(whereto)); + } + } + if (i < 0 || i != cc) { + if (i < 0) { + if (options & F_FLOOD && errno == ENOBUFS) { + usleep(FLOOD_BACKOFF); + return; + } + warn("sendto"); + } else { + warn("%s: partial write: %d of %d bytes", + hostname, i, cc); + } + } + ntransmitted++; + sntransmitted++; + if (!(options & F_QUIET) && options & F_FLOOD) + (void)write(STDOUT_FILENO, &DOT, 1); +} + +/* + * pr_pack -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +static void +pr_pack(char *buf, int cc, struct sockaddr_in *from, struct timeval *tv, + int tc) +{ + struct in_addr ina; + u_char *cp, *dp; + struct icmp *icp; + struct ip *ip; + const void *tp; + double triptime; + int dupflag, hlen, i, j, recv_len, seq; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + + /* Check the IP header */ + ip = (struct ip *)buf; + hlen = ip->ip_hl << 2; + recv_len = cc; + if (cc < hlen + ICMP_MINLEN) { + if (options & F_VERBOSE) + warn("packet too short (%d bytes) from %s", cc, + inet_ntoa(from->sin_addr)); + return; + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmp *)(buf + hlen); + if (icp->icmp_type == icmp_type_rsp) { + if (icp->icmp_id != ident) + return; /* 'Twas not our ECHO */ + ++nreceived; + triptime = 0.0; + if (timing) { + struct timeval tv1; + struct tv32 tv32; +#ifndef icmp_data + tp = &icp->icmp_ip; +#else + tp = icp->icmp_data; +#endif + tp = (const char *)tp + phdr_len; + + if (cc - ICMP_MINLEN - phdr_len >= sizeof(tv1)) { + /* Copy to avoid alignment problems: */ + memcpy(&tv32, tp, sizeof(tv32)); + tv1.tv_sec = ntohl(tv32.tv32_sec); + tv1.tv_usec = ntohl(tv32.tv32_usec); + tvsub(tv, &tv1); + triptime = ((double)tv->tv_sec) * 1000.0 + + ((double)tv->tv_usec) / 1000.0; + tsum += triptime; + tsumsq += triptime * triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } else + timing = 0; + } + + seq = ntohs(icp->icmp_seq); + + if (TST(seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_WAITTIME && triptime > waittime) { + ++nrcvtimeout; + return; + } + + if (options & F_FLOOD) + (void)write(STDOUT_FILENO, &BSPACE, 1); + else { + int seq_sent_len = send_len; + int seq_datalen = datalen; + + if (sweepmax != 0) { + /* + * When sweeping take in account the length of that + * was sent based on the sequence number + */ + seq_datalen = sweepmin + (seq / snpackets) * sweepincr; + seq_sent_len = icmp_len + seq_datalen; + } + if (options & F_PRTIME) + pr_currenttime(); + (void)printf("%d bytes from %s: icmp_seq=%u", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), + seq); + (void)printf(" ttl=%d", ip->ip_ttl); + if (timing) + (void)printf(" time=%.3f ms", triptime); + if (tc != -1) { + (void)printf(" tc=%d", tc); + } + if (dupflag && no_dup == 0) { + (void)printf(" (DUP!)"); + } + if (options & F_AUDIBLE) + (void)write(STDOUT_FILENO, &BBELL, 1); + if (options & F_MASK) { + /* Just prentend this cast isn't ugly */ + (void)printf(" mask=%s", + pr_addr(*(struct in_addr *)&(icp->icmp_mask))); + } + if (options & F_TIME) { + (void)printf(" tso=%s", pr_ntime(icp->icmp_otime)); + (void)printf(" tsr=%s", pr_ntime(icp->icmp_rtime)); + (void)printf(" tst=%s", pr_ntime(icp->icmp_ttime)); + } + if (recv_len != seq_sent_len) { + (void)printf( + "\nwrong total length %d instead of %d", + recv_len, seq_sent_len); + } + /* check the data */ + cp = (u_char*)&icp->icmp_data[phdr_len]; + dp = &outpack[ICMP_MINLEN + phdr_len]; + cc -= ICMP_MINLEN + phdr_len; + i = 0; + if (timing) { /* don't check variable timestamp */ + cp += TIMEVAL_LEN; + dp += TIMEVAL_LEN; + cc -= TIMEVAL_LEN; + i += TIMEVAL_LEN; + } + for (; i < seq_datalen && cc > 0; ++i, ++cp, ++dp, --cc) { + if (*cp != *dp) { + (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + (void)printf("\ncp:"); + cp = (u_char*)&icp->icmp_data[0]; + for (i = 0; i < seq_datalen; ++i, ++cp) { + if ((i % 16) == 0) + (void)printf("\n\t"); + (void)printf("%2x ", *cp); + } + (void)printf("\ndp:"); + cp = &outpack[ICMP_MINLEN]; + for (i = 0; i < seq_datalen; ++i, ++cp) { + if ((i % 16) == 0) + (void)printf("\n\t"); + (void)printf("%2x ", *cp); + } + break; + } + } + } + } else { + /* + * We've got something other than an ECHOREPLY. + * See if it's a reply to something that we sent. + * We can compare IP destination, protocol, + * and ICMP type and ID. + * + * Only print all the error messages if we are running + * as root to avoid leaking information not normally + * available to those not running as root. + */ +#ifndef icmp_data + struct ip *oip = &icp->icmp_ip; +#else + struct ip *oip = (struct ip *)icp->icmp_data; +#endif + struct icmp *oicmp = (struct icmp *)(oip + 1); + + if (((options & F_VERBOSE) && uid == 0) || + (!(options & F_QUIET2) && + (oip->ip_dst.s_addr == whereto.sin_addr.s_addr) && + (oip->ip_p == IPPROTO_ICMP) && + (oicmp->icmp_type == ICMP_ECHO) && + (oicmp->icmp_id == ident))) { + if (options & F_PRTIME) + pr_currenttime(); + (void)printf("%d bytes from %s: ", cc, + pr_addr(from->sin_addr)); + pr_icmph(icp); + } else + return; + } + + /* Display any IP options */ + cp = (u_char *)buf + sizeof(struct ip); + + for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) + switch (*cp) { + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + case IPOPT_SSRR: + (void)printf(*cp == IPOPT_LSRR ? + "\nLSRR: " : "\nSSRR: "); + j = cp[IPOPT_OLEN] - IPOPT_MINOFF + 1; + hlen -= 2; + cp += 2; + if (j >= INADDR_LEN && + j <= hlen - (int)sizeof(struct ip)) { + for (;;) { + bcopy(++cp, &ina.s_addr, INADDR_LEN); + if (ina.s_addr == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", + pr_addr(ina)); + hlen -= INADDR_LEN; + cp += INADDR_LEN - 1; + j -= INADDR_LEN; + if (j < INADDR_LEN) + break; + (void)putchar('\n'); + } + } else + (void)printf("\t(truncated route)\n"); + break; + case IPOPT_RR: + j = cp[IPOPT_OLEN]; /* get length */ + i = cp[IPOPT_OFFSET]; /* and pointer */ + hlen -= 2; + cp += 2; + if (i > j) + i = j; + i = i - IPOPT_MINOFF + 1; + if (i < 0 || i > (hlen - (int)sizeof(struct ip))) { + old_rrlen = 0; + continue; + } + if (i == old_rrlen + && !bcmp((char *)cp, old_rr, i) + && !(options & F_FLOOD)) { + (void)printf("\t(same route)"); + hlen -= i; + cp += i; + break; + } + old_rrlen = i; + bcopy((char *)cp, old_rr, i); + (void)printf("\nRR: "); + if (i >= INADDR_LEN && + i <= hlen - (int)sizeof(struct ip)) { + for (;;) { + bcopy(++cp, &ina.s_addr, INADDR_LEN); + if (ina.s_addr == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", + pr_addr(ina)); + hlen -= INADDR_LEN; + cp += INADDR_LEN - 1; + i -= INADDR_LEN; + if (i < INADDR_LEN) + break; + (void)putchar('\n'); + } + } else + (void)printf("\t(truncated route)"); + break; + case IPOPT_NOP: + (void)printf("\nNOP"); + break; + default: + (void)printf("\nunknown option %x", *cp); + break; + } + if (!(options & F_FLOOD)) { + (void)putchar('\n'); + (void)fflush(stdout); + } +} + +/* + * in_cksum -- + * Checksum routine for Internet Protocol family headers (C Version) + */ +u_short +in_cksum(u_short *addr, int len) +{ + int nleft, sum; + u_short *w; + union { + u_short us; + u_char uc[2]; + } last; + u_short answer; + + nleft = len; + sum = 0; + w = addr; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), we add + * sequential 16 bit words to it, and at the end, fold back all the + * carry bits from the top 16 bits into the lower 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + last.uc[0] = *(u_char *)w; + last.uc[1] = 0; + sum += last.us; + } + + /* add back carry outs from top 16 bits to low 16 bits */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return(answer); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +static void +tvsub(struct timeval *out, const struct timeval *in) +{ + + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * status -- + * Print out statistics when SIGINFO is received. + */ + +static void +status(int sig __unused) +{ + + siginfo_p = 1; +} + +static void +check_status(void) +{ + + if (siginfo_p) { + siginfo_p = 0; + (void)fprintf(stderr, "\r%ld/%ld packets received (%.1f%%)", + nreceived, ntransmitted, + ntransmitted ? nreceived * 100.0 / ntransmitted : 0.0); + if (nreceived && timing) + (void)fprintf(stderr, " %.3f min / %.3f avg / %.3f max", + tmin, tsum / (nreceived + nrepeats), tmax); + (void)fprintf(stderr, "\n"); + } +} + +/* + * finish -- + * Print out statistics, and give up. + */ +static void +finish(void) +{ + + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGALRM, SIG_IGN); + (void)putchar('\n'); + (void)fflush(stdout); + (void)printf("--- %s ping statistics ---\n", hostname); + (void)printf("%ld packets transmitted, ", ntransmitted); + (void)printf("%ld packets received, ", nreceived); + if (nrepeats) + (void)printf("+%ld duplicates, ", nrepeats); + if (ntransmitted) { + if (nreceived > ntransmitted) + (void)printf("-- somebody's printing up packets!"); + else + (void)printf("%.1f%% packet loss", + ((ntransmitted - nreceived) * 100.0) / + ntransmitted); + } + if (nrcvtimeout) + (void)printf(", %ld packets out of wait time", nrcvtimeout); + (void)putchar('\n'); + if (nreceived && timing) { + double n = nreceived + nrepeats; + double avg = tsum / n; + double vari = tsumsq / n - avg * avg; + (void)printf( + "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n", + tmin, avg, tmax, sqrt(vari)); + } + + if (nreceived) + exit(0); + else + exit(2); +} + +#ifdef notdef +static char *ttab[] = { + "Echo Reply", /* ip + seq + udata */ + "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ + "Source Quench", /* IP */ + "Redirect", /* redirect type, gateway, + IP */ + "Echo", + "Time Exceeded", /* transit, frag reassem + IP */ + "Parameter Problem", /* pointer + IP */ + "Timestamp", /* id + seq + three timestamps */ + "Timestamp Reply", /* " */ + "Info Request", /* id + sq */ + "Info Reply" /* " */ +}; +#endif + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +static void +pr_icmph(struct icmp *icp) +{ + + switch(icp->icmp_type) { + case ICMP_ECHOREPLY: + (void)printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_UNREACH: + switch(icp->icmp_code) { + case ICMP_UNREACH_NET: + (void)printf("Destination Net Unreachable\n"); + break; + case ICMP_UNREACH_HOST: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP_UNREACH_PROTOCOL: + (void)printf("Destination Protocol Unreachable\n"); + break; + case ICMP_UNREACH_PORT: + (void)printf("Destination Port Unreachable\n"); + break; + case ICMP_UNREACH_NEEDFRAG: + (void)printf("frag needed and DF set (MTU %d)\n", + ntohs(icp->icmp_nextmtu)); + break; + case ICMP_UNREACH_SRCFAIL: + (void)printf("Source Route Failed\n"); + break; + case ICMP_UNREACH_FILTER_PROHIB: + (void)printf("Communication prohibited by filter\n"); + break; + default: + (void)printf("Dest Unreachable, Bad Code: %d\n", + icp->icmp_code); + break; + } + /* Print returned IP header information */ +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_SOURCEQUENCH: + (void)printf("Source Quench\n"); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_REDIRECT: + switch(icp->icmp_code) { + case ICMP_REDIRECT_NET: + (void)printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + (void)printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + (void)printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + (void)printf("Redirect Type of Service and Host"); + break; + default: + (void)printf("Redirect, Bad Code: %d", icp->icmp_code); + break; + } + (void)printf("(New addr: %s)\n", inet_ntoa(icp->icmp_gwaddr)); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_ECHO: + (void)printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIMXCEED: + switch(icp->icmp_code) { + case ICMP_TIMXCEED_INTRANS: + (void)printf("Time to live exceeded\n"); + break; + case ICMP_TIMXCEED_REASS: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d\n", + icp->icmp_code); + break; + } +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_PARAMPROB: + (void)printf("Parameter problem: pointer = 0x%02x\n", + icp->icmp_hun.ih_pptr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_TSTAMP: + (void)printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TSTAMPREPLY: + (void)printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_IREQ: + (void)printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_IREQREPLY: + (void)printf("Information Reply\n"); + /* XXX ID + Seq */ + break; + case ICMP_MASKREQ: + (void)printf("Address Mask Request\n"); + break; + case ICMP_MASKREPLY: + (void)printf("Address Mask Reply\n"); + break; + case ICMP_ROUTERADVERT: + (void)printf("Router Advertisement\n"); + break; + case ICMP_ROUTERSOLICIT: + (void)printf("Router Solicitation\n"); + break; + default: + (void)printf("Bad ICMP type: %d\n", icp->icmp_type); + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +static void +pr_iph(struct ip *ip) +{ + u_char *cp; + int hlen; + + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + 20; /* point to options */ + + (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n"); + (void)printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len), + ntohs(ip->ip_id)); + (void)printf(" %1lx %04lx", + (u_long) (ntohl(ip->ip_off) & 0xe000) >> 13, + (u_long) ntohl(ip->ip_off) & 0x1fff); + (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, + ntohs(ip->ip_sum)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump any option bytes */ + while (hlen-- > 20) { + (void)printf("%02x", *cp++); + } + (void)putchar('\n'); +} + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +static char * +pr_addr(struct in_addr ina) +{ + struct hostent *hp; + static char buf[16 + 3 + MAXHOSTNAMELEN]; + + if ((options & F_NUMERIC) || + !(hp = gethostbyaddr((char *)&ina, 4, AF_INET))) + return inet_ntoa(ina); + else + (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name, + inet_ntoa(ina)); + return(buf); +} + +/* + * pr_retip -- + * Dump some info on a returned (via ICMP) IP packet. + */ +static void +pr_retip(struct ip *ip) +{ + u_char *cp; + int hlen; + + pr_iph(ip); + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + hlen; + + if (ip->ip_p == 6) + (void)printf("TCP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); + else if (ip->ip_p == 17) + (void)printf("UDP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); +} + +static char * +pr_ntime(n_time timestamp) +{ + static char buf[10]; + int hour, min, sec; + + sec = ntohl(timestamp) / 1000; + hour = sec / 60 / 60; + min = (sec % (60 * 60)) / 60; + sec = (sec % (60 * 60)) % 60; + + (void)snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour, min, sec); + + return (buf); +} + +static void +fill(char *bp, char *patp) +{ + char *cp; + int pat[16]; + u_int ii, jj, kk; + + for (cp = patp; *cp; cp++) { + if (!isxdigit(*cp)) + errx(EX_USAGE, + "patterns must be specified as hex digits"); + + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if (ii > 0) + for (kk = 0; kk <= maxpayload - (TIMEVAL_LEN + ii); kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + if (!(options & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + (void)printf("%02x", bp[jj] & 0xFF); + (void)printf("\n"); + } +} + +int +str2sotc(const char *str, bool *valid) +{ + int sotc = -1; + char *endptr; + + *valid = true; + + if (str == NULL || *str == '\0') + *valid = false; + else if (strcasecmp(str, "BK_SYS") == 0) + return SO_TC_BK_SYS; + else if (strcasecmp(str, "BK") == 0) + return SO_TC_BK; + else if (strcasecmp(str, "BE") == 0) + return SO_TC_BE; + else if (strcasecmp(str, "RD") == 0) + return SO_TC_RD; + else if (strcasecmp(str, "OAM") == 0) + return SO_TC_OAM; + else if (strcasecmp(str, "AV") == 0) + return SO_TC_AV; + else if (strcasecmp(str, "RV") == 0) + return SO_TC_RV; + else if (strcasecmp(str, "VI") == 0) + return SO_TC_VI; + else if (strcasecmp(str, "VO") == 0) + return SO_TC_VO; + else if (strcasecmp(str, "CTL") == 0) + return SO_TC_CTL; + else { + sotc = (int)strtol(str, &endptr, 0); + if (*endptr != '\0') + *valid = false; + } + return (sotc); +} + +int +str2netservicetype(const char *str, bool *valid) +{ + int svc = -1; + char *endptr; + + *valid = true; + + if (str == NULL || *str == '\0') + *valid = false; + else if (strcasecmp(str, "BK") == 0) + return NET_SERVICE_TYPE_BK; + else if (strcasecmp(str, "BE") == 0) + return NET_SERVICE_TYPE_BE; + else if (strcasecmp(str, "VI") == 0) + return NET_SERVICE_TYPE_VI; + else if (strcasecmp(str, "SIG") == 0) + return NET_SERVICE_TYPE_SIG; + else if (strcasecmp(str, "VO") == 0) + return NET_SERVICE_TYPE_VO; + else if (strcasecmp(str, "RV") == 0) + return NET_SERVICE_TYPE_RV; + else if (strcasecmp(str, "AV") == 0) + return NET_SERVICE_TYPE_AV; + else if (strcasecmp(str, "OAM") == 0) + return NET_SERVICE_TYPE_OAM; + else if (strcasecmp(str, "RD") == 0) + return NET_SERVICE_TYPE_RD; + else { + svc = (int)strtol(str, &endptr, 0); + if (*endptr != '\0') + *valid = false; + } + return (svc); +} + +u_int8_t +str2tos(const char *str, bool *valid) +{ + u_int8_t dscp = -1; + char *endptr; + + *valid = true; + + if (str == NULL || *str == '\0') + *valid = false; + else if (strcasecmp(str, "DF") == 0) + dscp = _DSCP_DF; + else if (strcasecmp(str, "EF") == 0) + dscp = _DSCP_EF; + else if (strcasecmp(str, "VA") == 0) + dscp = _DSCP_VA; + + else if (strcasecmp(str, "CS0") == 0) + dscp = _DSCP_CS0; + else if (strcasecmp(str, "CS1") == 0) + dscp = _DSCP_CS1; + else if (strcasecmp(str, "CS2") == 0) + dscp = _DSCP_CS2; + else if (strcasecmp(str, "CS3") == 0) + dscp = _DSCP_CS3; + else if (strcasecmp(str, "CS4") == 0) + dscp = _DSCP_CS4; + else if (strcasecmp(str, "CS5") == 0) + dscp = _DSCP_CS5; + else if (strcasecmp(str, "CS6") == 0) + dscp = _DSCP_CS6; + else if (strcasecmp(str, "CS7") == 0) + dscp = _DSCP_CS7; + + else if (strcasecmp(str, "AF11") == 0) + dscp = _DSCP_AF11; + else if (strcasecmp(str, "AF12") == 0) + dscp = _DSCP_AF12; + else if (strcasecmp(str, "AF13") == 0) + dscp = _DSCP_AF13; + else if (strcasecmp(str, "AF21") == 0) + dscp = _DSCP_AF21; + else if (strcasecmp(str, "AF22") == 0) + dscp = _DSCP_AF22; + else if (strcasecmp(str, "AF23") == 0) + dscp = _DSCP_AF23; + else if (strcasecmp(str, "AF31") == 0) + dscp = _DSCP_AF31; + else if (strcasecmp(str, "AF32") == 0) + dscp = _DSCP_AF32; + else if (strcasecmp(str, "AF33") == 0) + dscp = _DSCP_AF33; + else if (strcasecmp(str, "AF41") == 0) + dscp = _DSCP_AF41; + else if (strcasecmp(str, "AF42") == 0) + dscp = _DSCP_AF42; + else if (strcasecmp(str, "AF43") == 0) + dscp = _DSCP_AF43; + + else { + unsigned long val = strtoul(str, &endptr, 0); + if (*endptr != '\0' || val > 255) + *valid = false; + else + return ((u_int8_t)val); + } + /* DSCP occupies the 6 upper bits of the TOS field */ + return (dscp << 2); +} + +void +pr_currenttime(void) +{ + int s; + struct timeval tv; + + gettimeofday(&tv, NULL); + + s = (tv.tv_sec + thiszone) % 86400; + printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, + (u_int32_t)tv.tv_usec); +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define SECOPT " [-P policy]" +#else +#define SECOPT "" +#endif +static void +usage(void) +{ + + (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", +"usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize]", +" [-g sweepminsize] [-h sweepincrsize] [-i wait]", +" [-l preload] [-M mask | time] [-m ttl]" SECOPT " [-p pattern]", +" [-S src_addr] [-s packetsize] [-t timeout][-W waittime]", +" [-z tos] host", +" ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait]", +" [-l preload] [-M mask | time] [-m ttl]" SECOPT " [-p pattern] [-S src_addr]", +" [-s packetsize] [-T ttl] [-t timeout] [-W waittime]", +" [-z tos] mcast-group"); + (void)fprintf(stderr, "Apple specific options (to be specified before mcast-group or host like all options)\n"); + (void)fprintf(stderr, " -b boundif # bind the socket to the interface\n"); + (void)fprintf(stderr, " -k traffic_class # set traffic class socket option\n"); + (void)fprintf(stderr, " -K net_service_type # set traffic class socket options\n"); + (void)fprintf(stderr, " -apple-connect # call connect(2) in the socket\n"); + (void)fprintf(stderr, " -apple-time # display current time\n"); + exit(EX_USAGE); +} diff --git a/network_cmds/ping6.tproj/md5.c b/network_cmds/ping6.tproj/md5.c new file mode 100644 index 0000000..68a654b --- /dev/null +++ b/network_cmds/ping6.tproj/md5.c @@ -0,0 +1,308 @@ +/* $KAME: md5.c,v 1.3 2000/05/27 07:14:22 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/cdefs.h> +#include <sys/time.h> +#include <string.h> +#include "md5.h" + +#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s)))) + +#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z))) +#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z))) +#define H(X, Y, Z) ((X) ^ (Y) ^ (Z)) +#define I(X, Y, Z) ((Y) ^ ((X) | (~Z))) + +#define ROUND1(a, b, c, d, k, s, i) { \ + (a) = (a) + F((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define ROUND2(a, b, c, d, k, s, i) { \ + (a) = (a) + G((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define ROUND3(a, b, c, d, k, s, i) { \ + (a) = (a) + H((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define ROUND4(a, b, c, d, k, s, i) { \ + (a) = (a) + I((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define Sa 7 +#define Sb 12 +#define Sc 17 +#define Sd 22 + +#define Se 5 +#define Sf 9 +#define Sg 14 +#define Sh 20 + +#define Si 4 +#define Sj 11 +#define Sk 16 +#define Sl 23 + +#define Sm 6 +#define Sn 10 +#define So 15 +#define Sp 21 + +#define MD5_A0 0x67452301 +#define MD5_B0 0xefcdab89 +#define MD5_C0 0x98badcfe +#define MD5_D0 0x10325476 + +/* Integer part of 4294967296 times abs(sin(i)), where i is in radians. */ +static const u_int32_t T[65] = { + 0, + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, +}; + +static const u_int8_t md5_paddat[MD5_BUFLEN] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static void md5_calc __P((u_int8_t *, md5_ctxt *)); + +void md5_init(ctxt) + md5_ctxt *ctxt; +{ + ctxt->md5_n = 0; + ctxt->md5_i = 0; + ctxt->md5_sta = MD5_A0; + ctxt->md5_stb = MD5_B0; + ctxt->md5_stc = MD5_C0; + ctxt->md5_std = MD5_D0; + bzero(ctxt->md5_buf, sizeof(ctxt->md5_buf)); +} + +void md5_loop(ctxt, input, len) + md5_ctxt *ctxt; + u_int8_t *input; + u_int len; /* number of bytes */ +{ + u_int gap, i; + + ctxt->md5_n += len * 8; /* byte to bit */ + gap = MD5_BUFLEN - ctxt->md5_i; + + if (len >= gap) { + bcopy((void *)input, (void *)(ctxt->md5_buf + ctxt->md5_i), + gap); + md5_calc(ctxt->md5_buf, ctxt); + + for (i = gap; i + MD5_BUFLEN <= len; i += MD5_BUFLEN) { + md5_calc((u_int8_t *)(input + i), ctxt); + } + + ctxt->md5_i = len - i; + bcopy((void *)(input + i), (void *)ctxt->md5_buf, ctxt->md5_i); + } else { + bcopy((void *)input, (void *)(ctxt->md5_buf + ctxt->md5_i), + len); + ctxt->md5_i += len; + } +} + +void md5_pad(ctxt) + md5_ctxt *ctxt; +{ + u_int gap; + + /* Don't count up padding. Keep md5_n. */ + gap = MD5_BUFLEN - ctxt->md5_i; + if (gap > 8) { + bcopy((void *)md5_paddat, + (void *)(ctxt->md5_buf + ctxt->md5_i), + gap - sizeof(ctxt->md5_n)); + } else { + /* including gap == 8 */ + bcopy((void *)md5_paddat, (void *)(ctxt->md5_buf + ctxt->md5_i), + gap); + md5_calc(ctxt->md5_buf, ctxt); + bcopy((void *)(md5_paddat + gap), + (void *)ctxt->md5_buf, + MD5_BUFLEN - sizeof(ctxt->md5_n)); + } + + /* 8 byte word */ +#if BYTE_ORDER == LITTLE_ENDIAN + bcopy(&ctxt->md5_n8[0], &ctxt->md5_buf[56], 8); +#endif +#if BYTE_ORDER == BIG_ENDIAN + ctxt->md5_buf[56] = ctxt->md5_n8[7]; + ctxt->md5_buf[57] = ctxt->md5_n8[6]; + ctxt->md5_buf[58] = ctxt->md5_n8[5]; + ctxt->md5_buf[59] = ctxt->md5_n8[4]; + ctxt->md5_buf[60] = ctxt->md5_n8[3]; + ctxt->md5_buf[61] = ctxt->md5_n8[2]; + ctxt->md5_buf[62] = ctxt->md5_n8[1]; + ctxt->md5_buf[63] = ctxt->md5_n8[0]; +#endif + + md5_calc(ctxt->md5_buf, ctxt); +} + +void md5_result(digest, ctxt) + u_int8_t *digest; + md5_ctxt *ctxt; +{ + /* 4 byte words */ +#if BYTE_ORDER == LITTLE_ENDIAN + bcopy(&ctxt->md5_st8[0], digest, 16); +#endif +#if BYTE_ORDER == BIG_ENDIAN + digest[ 0] = ctxt->md5_st8[ 3]; digest[ 1] = ctxt->md5_st8[ 2]; + digest[ 2] = ctxt->md5_st8[ 1]; digest[ 3] = ctxt->md5_st8[ 0]; + digest[ 4] = ctxt->md5_st8[ 7]; digest[ 5] = ctxt->md5_st8[ 6]; + digest[ 6] = ctxt->md5_st8[ 5]; digest[ 7] = ctxt->md5_st8[ 4]; + digest[ 8] = ctxt->md5_st8[11]; digest[ 9] = ctxt->md5_st8[10]; + digest[10] = ctxt->md5_st8[ 9]; digest[11] = ctxt->md5_st8[ 8]; + digest[12] = ctxt->md5_st8[15]; digest[13] = ctxt->md5_st8[14]; + digest[14] = ctxt->md5_st8[13]; digest[15] = ctxt->md5_st8[12]; +#endif +} + +#if BYTE_ORDER == BIG_ENDIAN +u_int32_t X[16]; +#endif + +static void md5_calc(b64, ctxt) + u_int8_t *b64; + md5_ctxt *ctxt; +{ + u_int32_t A = ctxt->md5_sta; + u_int32_t B = ctxt->md5_stb; + u_int32_t C = ctxt->md5_stc; + u_int32_t D = ctxt->md5_std; +#if BYTE_ORDER == LITTLE_ENDIAN + u_int32_t *X = (u_int32_t *)b64; +#endif +#if BYTE_ORDER == BIG_ENDIAN + /* 4 byte words */ + /* what a brute force but fast! */ + u_int8_t *y = (u_int8_t *)X; + y[ 0] = b64[ 3]; y[ 1] = b64[ 2]; y[ 2] = b64[ 1]; y[ 3] = b64[ 0]; + y[ 4] = b64[ 7]; y[ 5] = b64[ 6]; y[ 6] = b64[ 5]; y[ 7] = b64[ 4]; + y[ 8] = b64[11]; y[ 9] = b64[10]; y[10] = b64[ 9]; y[11] = b64[ 8]; + y[12] = b64[15]; y[13] = b64[14]; y[14] = b64[13]; y[15] = b64[12]; + y[16] = b64[19]; y[17] = b64[18]; y[18] = b64[17]; y[19] = b64[16]; + y[20] = b64[23]; y[21] = b64[22]; y[22] = b64[21]; y[23] = b64[20]; + y[24] = b64[27]; y[25] = b64[26]; y[26] = b64[25]; y[27] = b64[24]; + y[28] = b64[31]; y[29] = b64[30]; y[30] = b64[29]; y[31] = b64[28]; + y[32] = b64[35]; y[33] = b64[34]; y[34] = b64[33]; y[35] = b64[32]; + y[36] = b64[39]; y[37] = b64[38]; y[38] = b64[37]; y[39] = b64[36]; + y[40] = b64[43]; y[41] = b64[42]; y[42] = b64[41]; y[43] = b64[40]; + y[44] = b64[47]; y[45] = b64[46]; y[46] = b64[45]; y[47] = b64[44]; + y[48] = b64[51]; y[49] = b64[50]; y[50] = b64[49]; y[51] = b64[48]; + y[52] = b64[55]; y[53] = b64[54]; y[54] = b64[53]; y[55] = b64[52]; + y[56] = b64[59]; y[57] = b64[58]; y[58] = b64[57]; y[59] = b64[56]; + y[60] = b64[63]; y[61] = b64[62]; y[62] = b64[61]; y[63] = b64[60]; +#endif + + ROUND1(A, B, C, D, 0, Sa, 1); ROUND1(D, A, B, C, 1, Sb, 2); + ROUND1(C, D, A, B, 2, Sc, 3); ROUND1(B, C, D, A, 3, Sd, 4); + ROUND1(A, B, C, D, 4, Sa, 5); ROUND1(D, A, B, C, 5, Sb, 6); + ROUND1(C, D, A, B, 6, Sc, 7); ROUND1(B, C, D, A, 7, Sd, 8); + ROUND1(A, B, C, D, 8, Sa, 9); ROUND1(D, A, B, C, 9, Sb, 10); + ROUND1(C, D, A, B, 10, Sc, 11); ROUND1(B, C, D, A, 11, Sd, 12); + ROUND1(A, B, C, D, 12, Sa, 13); ROUND1(D, A, B, C, 13, Sb, 14); + ROUND1(C, D, A, B, 14, Sc, 15); ROUND1(B, C, D, A, 15, Sd, 16); + + ROUND2(A, B, C, D, 1, Se, 17); ROUND2(D, A, B, C, 6, Sf, 18); + ROUND2(C, D, A, B, 11, Sg, 19); ROUND2(B, C, D, A, 0, Sh, 20); + ROUND2(A, B, C, D, 5, Se, 21); ROUND2(D, A, B, C, 10, Sf, 22); + ROUND2(C, D, A, B, 15, Sg, 23); ROUND2(B, C, D, A, 4, Sh, 24); + ROUND2(A, B, C, D, 9, Se, 25); ROUND2(D, A, B, C, 14, Sf, 26); + ROUND2(C, D, A, B, 3, Sg, 27); ROUND2(B, C, D, A, 8, Sh, 28); + ROUND2(A, B, C, D, 13, Se, 29); ROUND2(D, A, B, C, 2, Sf, 30); + ROUND2(C, D, A, B, 7, Sg, 31); ROUND2(B, C, D, A, 12, Sh, 32); + + ROUND3(A, B, C, D, 5, Si, 33); ROUND3(D, A, B, C, 8, Sj, 34); + ROUND3(C, D, A, B, 11, Sk, 35); ROUND3(B, C, D, A, 14, Sl, 36); + ROUND3(A, B, C, D, 1, Si, 37); ROUND3(D, A, B, C, 4, Sj, 38); + ROUND3(C, D, A, B, 7, Sk, 39); ROUND3(B, C, D, A, 10, Sl, 40); + ROUND3(A, B, C, D, 13, Si, 41); ROUND3(D, A, B, C, 0, Sj, 42); + ROUND3(C, D, A, B, 3, Sk, 43); ROUND3(B, C, D, A, 6, Sl, 44); + ROUND3(A, B, C, D, 9, Si, 45); ROUND3(D, A, B, C, 12, Sj, 46); + ROUND3(C, D, A, B, 15, Sk, 47); ROUND3(B, C, D, A, 2, Sl, 48); + + ROUND4(A, B, C, D, 0, Sm, 49); ROUND4(D, A, B, C, 7, Sn, 50); + ROUND4(C, D, A, B, 14, So, 51); ROUND4(B, C, D, A, 5, Sp, 52); + ROUND4(A, B, C, D, 12, Sm, 53); ROUND4(D, A, B, C, 3, Sn, 54); + ROUND4(C, D, A, B, 10, So, 55); ROUND4(B, C, D, A, 1, Sp, 56); + ROUND4(A, B, C, D, 8, Sm, 57); ROUND4(D, A, B, C, 15, Sn, 58); + ROUND4(C, D, A, B, 6, So, 59); ROUND4(B, C, D, A, 13, Sp, 60); + ROUND4(A, B, C, D, 4, Sm, 61); ROUND4(D, A, B, C, 11, Sn, 62); + ROUND4(C, D, A, B, 2, So, 63); ROUND4(B, C, D, A, 9, Sp, 64); + + ctxt->md5_sta += A; + ctxt->md5_stb += B; + ctxt->md5_stc += C; + ctxt->md5_std += D; +} diff --git a/network_cmds/ping6.tproj/md5.h b/network_cmds/ping6.tproj/md5.h new file mode 100644 index 0000000..b0752af --- /dev/null +++ b/network_cmds/ping6.tproj/md5.h @@ -0,0 +1,75 @@ +/* $KAME: md5.h,v 1.1 2000/05/27 06:18:21 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NETINET6_MD5_H_ +#define _NETINET6_MD5_H_ + +#define MD5_BUFLEN 64 + +typedef struct { + union { + u_int32_t md5_state32[4]; + u_int8_t md5_state8[16]; + } md5_st; + +#define md5_sta md5_st.md5_state32[0] +#define md5_stb md5_st.md5_state32[1] +#define md5_stc md5_st.md5_state32[2] +#define md5_std md5_st.md5_state32[3] +#define md5_st8 md5_st.md5_state8 + + union { + u_int64_t md5_count64; + u_int8_t md5_count8[8]; + } md5_count; +#define md5_n md5_count.md5_count64 +#define md5_n8 md5_count.md5_count8 + + u_int md5_i; + u_int8_t md5_buf[MD5_BUFLEN]; +} md5_ctxt; + +extern void md5_init __P((md5_ctxt *)); +extern void md5_loop __P((md5_ctxt *, u_int8_t *, u_int)); +extern void md5_pad __P((md5_ctxt *)); +extern void md5_result __P((u_int8_t *, md5_ctxt *)); + +/* compatibility */ +#define MD5_CTX md5_ctxt +#define MD5Init(x) md5_init((x)) +#define MD5Update(x, y, z) md5_loop((x), (y), (z)) +#define MD5Final(x, y) \ +do { \ + md5_pad((y)); \ + md5_result((x), (y)); \ +} while (0) + +#endif /* ! _NETINET6_MD5_H_*/ diff --git a/network_cmds/ping6.tproj/ping6.8 b/network_cmds/ping6.tproj/ping6.8 new file mode 100644 index 0000000..7ec0b70 --- /dev/null +++ b/network_cmds/ping6.tproj/ping6.8 @@ -0,0 +1,625 @@ +.\" Copyright (c) 2002-2013 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd March 29, 2013 +.Dt PING6 8 +.Os +.Sh NAME +.Nm ping6 +.Nd send +.Tn ICMPv6 ECHO_REQUEST +packets to network hosts +.Sh SYNOPSIS +.Nm +.\" without ipsec, or new ipsec +.Op Fl CDdfHmnNoqtvwW +.\" old ipsec +.\" .Op Fl ADdEfmnNqRtvwW +.Bk -words +.Op Fl a Ar addrtype +.Ek +.Bk -words +.Op Fl b Ar bufsiz +.Ek +.Bk -words +.Op Fl B Ar boundif +.Ek +.Bk -words +.Op Fl c Ar count +.Ek +.Bk -words +.Op Fl G Ar sweepmaxsize[,sweepminsize[,sweepincrsize]] +.Ek +.Bk -words +.Op Fl g Ar gateway +.Ek +.Bk -words +.Op Fl G Ar sweep +.Ek +.Bk -words +.Op Fl h Ar hoplimit +.Ek +.Bk -words +.Op Fl I Ar interface +.Ek +.Bk -words +.Op Fl i Ar wait +.Ek +.Bk -words +.Op Fl k Ar trafficclass +.Ek +.Bk -words +.Op Fl K Ar netservicetype +.Ek +.Bk -words +.Op Fl l Ar preload +.Ek +.Bk -words +.\" new ipsec +.Op Fl P Ar policy +.Ek +.Bk -words +.Op Fl p Ar pattern +.Ek +.Bk -words +.Op Fl S Ar sourceaddr +.Ek +.Bk -words +.Op Fl s Ar packetsize +.Ek +.Bk -words +.Op Fl z Ar tclass +.Ek +.Bk -words +.Op Fl Fl apple-connect +.Ek +.Bk -words +.Op Fl Fl apple-time +.Ek +.Bk -words +.Op Ar hops ... +.Ek +.Bk -words +.Ar host +.Ek +.Sh DESCRIPTION +The +.Nm +utility uses the +.Tn ICMPv6 +protocol's mandatory +.Tn ICMP6_ECHO_REQUEST +datagram to elicit an +.Tn ICMP6_ECHO_REPLY +from a host or gateway. +.Tn ICMP6_ECHO_REQUEST +datagrams (``pings'') have an IPv6 header, +and +.Tn ICMPv6 +header formatted as documented in RFC2463. +The options are as follows: +.Bl -tag -width Ds +.\" old ipsec +.\" .It Fl A +.\" Enables transport-mode IPsec authentication header +.\" (experimental). +.It Fl a Ar addrtype +Generate ICMPv6 Node Information Node Addresses query, rather than echo-request. +.Ar addrtype +must be a string constructed of the following characters. +.Bl -tag -width Ds -compact +.It Ic a +requests unicast addresses from all of the responder's interfaces. +If the character is omitted, +only those addresses which belong to the interface which has the +responder's address are requests. +.It Ic c +requests responder's IPv4-compatible and IPv4-mapped addresses. +.It Ic g +requests responder's global-scope addresses. +.It Ic s +requests responder's site-local addresses. +.It Ic l +requests responder's link-local addresses. +.It Ic A +requests responder's anycast addresses. +Without this character, the responder will return unicast addresses only. +With this character, the responder will return anycast addresses only. +Note that the specification does not specify how to get responder's +anycast addresses. +This is an experimental option. +.El +.It Fl b Ar bufsiz +Set socket buffer size. +.It Fl B Ar boundif +Bind the socket to interface +This option is an Apple addition. +.Ar boundif +for sending. +.It Fl C +Prohibit the socket from using the cellular network interface. +.It Fl c Ar count +Stop after sending +(and receiving) +.Ar count +.Tn ECHO_RESPONSE +packets. +If this option is specified in conjunction with ping sweeps, +each sweep will consist of +.Ar count +packets. +.It Fl D +Disable IPv6 fragmentation. +.It Fl d +Set the +.Dv SO_DEBUG +option on the socket being used. +.\" .It Fl E +.\" Enables transport-mode IPsec encapsulated security payload +.\" (experimental). +.It Fl f +Flood ping. +Outputs packets as fast as they come back or one hundred times per second, +whichever is more. +For every +.Tn ECHO_REQUEST +sent a period +.Dq \&. +is printed, while for every +.Tn ECHO_REPLY +received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +Only the super-user may use this option. +.Bf -emphasis +This can be very hard on a network and should be used with caution. +.Ef +.It Fl G Ar sweepmaxsize[,sweepminsize[,sweepincrsize]] +.Ar sweepmaxsize +specifies the maximum size of the payload when sending sweeping +pings and is required for sweeps. +.Ar sweepminsize +specifies the size of the payload to start with when sending +sweeping pings -- the default value is 0. +.Ar sweepincrsize +specifies the number of bytes to increment the size of the payload +after each sweep when sending sweeping pings -- the default value +is 1. +This option is an Apple addition. +.It Fl g Ar gateway +Specifies to use +.Ar gateway +as the next hop to the destination. +The gateway must be a neighbor of the sending node. +.It Fl H +Specifies to try reverse-lookup of IPv6 addresses. +The +.Nm +utility does not try reverse-lookup unless the option is specified. +.It Fl h Ar hoplimit +Set the IPv6 hoplimit. +.It Fl I Ar interface +Source packets with the given interface address. +This flag applies if the ping destination is a multicast address, +or link-local/site-local unicast address. +.It Fl i Ar wait +Wait +.Ar wait +seconds +.Em between sending each packet . +The default is to wait for one second between each packet. +The wait time may be fractional, but only the super-user may specify +values less than 0.1 second. +This option is incompatible with the +.Fl f +option. +.It Fl k Ar trafficclass +Specifies the traffic class to use for sending ICMPv6 packets. +The supported traffic classes are +BK_SYS, BK, BE, RD, OAM, AV, RV, VI, VO and CTL. +By default +.Nm +uses the control traffic class (CTL). +This option is an Apple addition. +.It Fl K Ar netservicetype +Specifies the network service type to use for sending ICMPv6 packets. +The supported network service type are BK_SYS, BK, BE, RV, AV, RD, OAM, VI, SIG and VO. +Note this overrides the default traffic class (-k can still be specified after -K to use both). +This option is an Apple addition. +.It Fl l Ar preload +If +.Ar preload +is specified, +.Nm +sends that many packets as fast as possible before falling into its normal +mode of behavior. +Only the super-user may use this option. +.It Fl m +By default, +.Nm +asks the kernel to fragment packets to fit into the minimum IPv6 MTU. +The +.Fl m +option +will suppress the behavior in the following two levels: +when the option is specified once, the behavior will be disabled for +unicast packets. +When the option is more than once, it will be disabled for both +unicast and multicast packets. +.It Fl n +Numeric output only. +No attempt will be made to lookup symbolic names from addresses in the reply. +.It Fl N +Probe node information multicast group +.Pq Li ff02::2:xxxx:xxxx . +.Ar host +must be string hostname of the target +(must not be a numeric IPv6 address). +Node information multicast group will be computed based on given +.Ar host , +and will be used as the final destination. +Since node information multicast group is a link-local multicast group, +outgoing interface needs to be specified by +.Fl I +option. +.It Fl o +Exit successfully after receiving one reply packet. +.It Fl p Ar pattern +You may specify up to 16 +.Dq pad +bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, +.Dq Li \-p ff +will cause the sent packet to be filled with all +ones. +.\" new ipsec +.It Fl P Ar policy +.Ar policy +specifies IPsec policy to be used for the probe. +.It Fl q +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. +.It Fl r +Audible. +Include a bell +.Tn ( ASCII +0x07) +character in the output when any packet is received. +.It Fl R +Audible. +Output a bell +.Tn ( ASCII +0x07) +character when no packet is received before the next packet +is transmitted. +To cater for round-trip times that are longer than the interval +between transmissions, further missing packets cause a bell only +if the maximum number of unreceived packets has increased. +.It Fl S Ar sourceaddr +Specifies the source address of request packets. +The source address must be one of the unicast addresses of the sending node, +and must be numeric. +.It Fl s Ar packetsize +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 +.Tn ICMP +data bytes when combined +with the 8 bytes of +.Tn ICMP +header data. +You may need to specify +.Fl b +as well to extend socket buffer size. +.It Fl t +Generate ICMPv6 Node Information supported query types query, +rather than echo-request. +.Fl s +has no effect if +.Fl t +is specified. +.It Fl v +Verbose output. +.Tn ICMP +packets other than +.Tn ECHO_RESPONSE +that are received are listed. +.It Fl w +Generate ICMPv6 Node Information DNS Name query, rather than echo-request. +.Fl s +has no effect if +.Fl w +is specified. +.It Fl W +Same as +.Fl w , +but with old packet format based on 03 draft. +This option is present for backward compatibility. +.Fl s +has no effect if +.Fl w +is specified. +.It Fl z Ar tclass +Use the specified traffic class. +.It Fl Fl apple-connect +Connects the socket to the destination address. +This option is an Apple addition. +.It Fl Fl apple-time +Prints the time a packet was received. +This option is an Apple addition. +.It Ar hops +IPv6 addresses for intermediate nodes, +which will be put into type 0 routing header. +.It Ar host +IPv6 address of the final destination node. +.El +.Pp +When using +.Nm +for fault isolation, it should first be run on the local host, to verify +that the local network interface is up and running. +Then, hosts and gateways further and further away should be +.Dq pinged . +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the round-trip time statistics. +When the specified number of packets have been sent +(and received) +or if the program is terminated with a +.Dv SIGINT , +a brief summary is displayed, showing the number of packets sent and +received, and the minimum, mean, maximum, and standard deviation of +the round-trip times. +.Pp +If +.Nm +receives a +.Dv SIGINFO +(see the +.Cm status +argument for +.Xr stty 1 ) +signal, the current number of packets sent and received, and the +minimum, mean, maximum, and standard deviation of the round-trip times +will be written to the standard output in the same format as the +standard completion message. +.Pp +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +.Nm +during normal operations or from automated scripts. +.\" .Sh ICMP PACKET DETAILS +.\" An IP header without options is 20 bytes. +.\" An +.\" .Tn ICMP +.\" .Tn ECHO_REQUEST +.\" packet contains an additional 8 bytes worth of +.\" .Tn ICMP +.\" header followed by an arbitrary amount of data. +.\" When a +.\" .Ar packetsize +.\" is given, this indicated the size of this extra piece of data +.\" (the default is 56). +.\" Thus the amount of data received inside of an IP packet of type +.\" .Tn ICMP +.\" .Tn ECHO_REPLY +.\" will always be 8 bytes more than the requested data space +.\" (the +.\" .Tn ICMP +.\" header). +.\" .Pp +.\" If the data space is at least eight bytes large, +.\" .Nm +.\" uses the first eight bytes of this space to include a timestamp which +.\" it uses in the computation of round trip times. +.\" If less than eight bytes of pad are specified, no round trip times are +.\" given. +.Sh DUPLICATE AND DAMAGED PACKETS +The +.Nm +utility will report duplicate and damaged packets. +Duplicate packets should never occur when pinging a unicast address, +and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely +(if ever) +a good sign, although the presence of low levels of duplicates may not +always be cause for alarm. +Duplicates are expected when pinging a broadcast or multicast address, +since they are not really duplicates but replies from different hosts +to the same request. +.Pp +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +.Nm +packet's path +(in the network or in the hosts). +.Sh TRYING DIFFERENT DATA PATTERNS +The +(inter)network +layer should never treat packets differently depending on the data +contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that does not have sufficient +.Dq transitions , +such as all ones or all zeros, or a pattern right at the edge, such as +almost all zeros. +It is not +necessarily enough to specify a data pattern of all zeros (for example) +on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. +.Pp +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either +cannot +be sent across your network or that takes much longer to transfer than +other similar length files. +You can then examine this file for repeated patterns that you can test +using the +.Fl p +option of +.Nm . +.Sh EXIT STATUS +The +.Nm +utility returns 0 on success (the host is alive), +2 if the transmission was successful but no responses were received, +any other non-zero value if the arguments are incorrect or +another error has occurred. +.Sh EXAMPLES +Normally, +.Nm +works just like +.Xr ping 8 +would work; the following will send ICMPv6 echo request to +.Li dst.foo.com . +.Bd -literal -offset indent +ping6 -n dst.foo.com +.Ed +.Pp +The following will probe hostnames for all nodes on the network link attached to +.Li wi0 +interface. +The address +.Li ff02::1 +is named the link-local all-node multicast address, and the packet would +reach every node on the network link. +.Bd -literal -offset indent +ping6 -w ff02::1%wi0 +.Ed +.Pp +The following will probe addresses assigned to the destination node, +.Li dst.foo.com . +.Bd -literal -offset indent +ping6 -a agl dst.foo.com +.Ed +.Sh SEE ALSO +.Xr netstat 1 , +.Xr icmp6 4 , +.Xr inet6 4 , +.Xr ip6 4 , +.Xr ifconfig 8 , +.Xr ping 8 , +.Xr routed 8 , +.Xr traceroute 8 , +.Xr traceroute6 8 +.Rs +.%A A. Conta +.%A S. Deering +.%T "Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification" +.%N RFC2463 +.%D December 1998 +.Re +.Rs +.%A Matt Crawford +.%T "IPv6 Node Information Queries" +.%N draft-ietf-ipngwg-icmp-name-lookups-09.txt +.%D May 2002 +.%O work in progress material +.Re +.Sh HISTORY +The +.Xr ping 8 +utility appeared in +.Bx 4.3 . +The +.Nm +utility with IPv6 support first appeared in the WIDE Hydrangea IPv6 +protocol stack kit. +.Pp +IPv6 and IPsec support based on the KAME Project +.Pq Pa http://www.kame.net/ +stack was initially integrated into +.Fx 4.0 . +.Sh BUGS +The +.Nm +utility +is intentionally separate from +.Xr ping 8 . +.Pp +There have been many discussions on why we separate +.Nm +and +.Xr ping 8 . +Some people argued that it would be more convenient to uniform the +ping command for both IPv4 and IPv6. +The followings are an answer to the request. +.Pp +From a developer's point of view: +since the underling raw sockets API is totally different between IPv4 +and IPv6, we would end up having two types of code base. +There would actually be less benefit to uniform the two commands +into a single command from the developer's standpoint. +.Pp +From an operator's point of view: unlike ordinary network applications +like remote login tools, we are usually aware of address family when using +network management tools. +We do not just want to know the reachability to the host, but want to know the +reachability to the host via a particular network protocol such as +IPv6. +Thus, even if we had a unified +.Xr ping 8 +command for both IPv4 and IPv6, we would usually type a +.Fl 6 +or +.Fl 4 +option (or something like those) to specify the particular address family. +This essentially means that we have two different commands. diff --git a/network_cmds/ping6.tproj/ping6.c b/network_cmds/ping6.tproj/ping6.c new file mode 100644 index 0000000..7890e66 --- /dev/null +++ b/network_cmds/ping6.tproj/ping6.c @@ -0,0 +1,3340 @@ +/* + * Copyright (c) 2002-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ +/* + * NOTE: + * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics + * as IPV6_PKTINFO. Some people object it (sin6_scope_id specifies *link* + * while IPV6_PKTINFO specifies *interface*. Link is defined as collection of + * network attached to 1 or more interfaces) + */ + +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sysexits.h> +#include <getopt.h> + +#ifdef IPSEC +#include <netinet6/ah.h> +#include <netinet6/ipsec.h> +#endif + +#include "md5.h" + +struct tv32 { + u_int32_t tv32_sec; + u_int32_t tv32_usec; +}; + +#define MAXPACKETLEN 131072 +#define IP6LEN 40 +#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */ +#define ICMP6ECHOTMLEN sizeof(struct tv32) +#define ICMP6_NIQLEN (ICMP6ECHOLEN + 8) +# define CONTROLLEN 10240 /* ancillary data buffer size RFC3542 20.1 */ +/* FQDN case, 64 bits of nonce + 32 bits ttl */ +#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12) +#define EXTRA 256 /* for AH and various other headers. weird. */ +#define DEFDATALEN ICMP6ECHOTMLEN +#define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN +#define NROUTES 9 /* number of record route slots */ +#define MAXWAIT 10000 /* max ms to wait for response */ +#define MAXALARM (60 * 60) /* max seconds for alarm timeout */ + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +#define F_FLOOD 0x0001 +#define F_INTERVAL 0x0002 +#define F_PINGFILLED 0x0008 +#define F_QUIET 0x0010 +#define F_RROUTE 0x0020 +#define F_SO_DEBUG 0x0040 +#define F_PRTIME 0x0080 +#define F_VERBOSE 0x0100 +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +#define F_POLICY 0x0400 +#else +#define F_AUTHHDR 0x0200 +#define F_ENCRYPT 0x0400 +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ +#define F_NODEADDR 0x0800 +#define F_FQDN 0x1000 +#define F_INTERFACE 0x2000 +#define F_SRCADDR 0x4000 +#define F_HOSTNAME 0x10000 +#define F_FQDNOLD 0x20000 +#define F_NIGROUP 0x40000 +#define F_SUPTYPES 0x80000 +#define F_NOMINMTU 0x100000 +#define F_ONCE 0x200000 +#define F_AUDIBLE 0x400000 +#define F_MISSED 0x800000 +#define F_DONTFRAG 0x1000000 +#define F_SWEEP 0x2000000 +#define F_CONNECT 0x4000000 +#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) +#define F_WAITTIME 0x8000000 +u_int options; + +static int longopt_flag = 0; + +#define LOF_CONNECT 0x01 +#define LOF_PRTIME 0x02 + +static const struct option longopts[] = { + { "apple-connect", no_argument, &longopt_flag, LOF_CONNECT }, + { "apple-time", no_argument, &longopt_flag, LOF_PRTIME }, + { NULL, 0, NULL, 0 } +}; + +#define IN6LEN sizeof(struct in6_addr) +#define SA6LEN sizeof(struct sockaddr_in6) +#define DUMMY_PORT 10101 + +#define SIN6(s) ((struct sockaddr_in6 *)(s)) + +#define MAXTOS 255 +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 8192) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct addrinfo *res; +struct sockaddr_in6 dst; /* who to ping6 */ +struct sockaddr_in6 src; /* src addr of this packet */ +socklen_t srclen; +int datalen = DEFDATALEN; +int s; /* socket file descriptor */ +u_char outpack[MAXPACKETLEN]; +char BSPACE = '\b'; /* characters written for flood */ +char BBELL = '\a'; /* characters written for AUDIBLE */ +char DOT = '.'; +char *hostname; +int ident; /* process id to identify our packets */ +u_int8_t nonce[8]; /* nonce field for node information */ +int hoplimit = -1; /* hoplimit */ +int pathmtu = 0; /* path MTU for the destination. 0 = unspec. */ +u_char *packet = NULL; +struct cmsghdr *cm = NULL; +char *boundif; +unsigned int ifscope; +int nocell; + +/* counters */ +long nmissedmax; /* max value of ntransmitted - nreceived - 1 */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +static int interval = 1000; /* interval between packets in ms */ +static int waittime = MAXWAIT; /* timeout for each packet */ +static long nrcvtimeout = 0; /* # of packets we got back after waittime */ + +long snpackets = 0; /* max packets to transmit in one sweep */ +long sntransmitted = 0; /* # of packets we sent in this sweep */ +int sweepmax = 0; /* max value of payload in sweep */ +int sweepmin = 0; /* start value of payload in sweep */ +int sweepincr = 1; /* payload increment in sweep */ + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ +double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ + +/* for node addresses */ +u_short naflags; + +/* for ancillary data(advanced API) */ +struct msghdr smsghdr; +struct iovec smsgiov[2]; +char *scmsg = NULL; + +volatile sig_atomic_t seenalrm; +volatile sig_atomic_t seenint; +#ifdef SIGINFO +volatile sig_atomic_t seeninfo; +#endif + +int rcvtclass = 0; + +int use_sendmsg = 0; +int use_recvmsg = 0; +int so_traffic_class = SO_TC_CTL; /* use control class, by default */ +int net_service_type = -1; + +int32_t thiszone; /* seconds offset from gmt to local time */ +extern int32_t gmt2local(time_t); +static void pr_currenttime(void); + +int main(int, char *[]); +void fill(char *, char *); +int get_hoplim(struct msghdr *); +int get_pathmtu(struct msghdr *); +int get_tclass(struct msghdr *); +int get_so_traffic_class(struct msghdr *); +struct in6_pktinfo *get_rcvpktinfo(struct msghdr *); +void onsignal(int); +void onint(int); +size_t pingerlen(void); +int pinger(void); +const char *pr_addr(struct sockaddr *, int); +void pr_icmph(struct icmp6_hdr *, u_char *); +void pr_iph(struct ip6_hdr *); +void pr_suptypes(struct icmp6_nodeinfo *, size_t); +void pr_nodeaddr(struct icmp6_nodeinfo *, int); +int myechoreply(const struct icmp6_hdr *); +int mynireply(const struct icmp6_nodeinfo *); +char *dnsdecode(const u_char **, const u_char *, const u_char *, + char *, size_t); +void pr_pack(u_char *, int, struct msghdr *); +void pr_exthdrs(struct msghdr *); +void pr_ip6opt(void *, size_t); +void pr_rthdr(void *, size_t); +int pr_bitrange(u_int32_t, int, int); +void pr_retip(struct ip6_hdr *, u_char *); +void summary(void); +void tvsub(struct timeval *, struct timeval *); +int setpolicy(int, char *); +char *nigroup(char *, int); +static int str2sotc(const char *, bool *); +static int str2netservicetype(const char *, bool *); +static u_int8_t str2tclass(const char *, bool *); +void usage(void); + +int +main(int argc, char *argv[]) +{ + struct timeval last, intvl; + struct sockaddr_in6 from; + struct addrinfo hints; + int cc, i; + int almost_done, ch, hold, packlen, preload, optval, ret_ga; + int nig_oldmcprefix = -1; + u_char *datap; + char *e, *target, *ifname = NULL, *gateway = NULL; + int ip6optlen = 0; + struct cmsghdr *scmsgp = NULL; +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + u_long lsockbufsize; + int sockbufsize = 0; +#endif + int usepktinfo = 0; + struct in6_pktinfo *pktinfo = NULL; +#ifdef USE_RFC2292BIS + struct ip6_rthdr *rthdr = NULL; +#endif +#ifdef IPSEC_POLICY_IPSEC + char *policy_in = NULL; + char *policy_out = NULL; +#endif + double t; + u_long alarmtimeout; + size_t rthlen; +#ifdef IPV6_USE_MIN_MTU + int mflag = 0; +#endif + /* T_CLASS value -1 means default, so -2 means do not bother */ + int tclass = -2; + bool valid; + + /* just to be sure */ + memset(&smsghdr, 0, sizeof(smsghdr)); + memset(&smsgiov, 0, sizeof(smsgiov)); + + preload = 0; + datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; +#ifndef IPSEC +#define ADDOPTS +#else +#ifdef IPSEC_POLICY_IPSEC +#define ADDOPTS "P:" +#else +#define ADDOPTS "AE" +#endif /*IPSEC_POLICY_IPSEC*/ +#endif + while ((ch = getopt_long(argc, argv, + "a:b:B:Cc:DdfHG:g:h:I:i:k:K:l:mnNop:qrRS:s:tvwWz:" ADDOPTS, + longopts, NULL)) != -1) { +#undef ADDOPTS + switch (ch) { + case 'a': + { + char *cp; + + options &= ~F_NOUSERDATA; + options |= F_NODEADDR; + for (cp = optarg; *cp != '\0'; cp++) { + switch (*cp) { + case 'a': + naflags |= NI_NODEADDR_FLAG_ALL; + break; + case 'c': + case 'C': + naflags |= NI_NODEADDR_FLAG_COMPAT; + break; + case 'l': + case 'L': + naflags |= NI_NODEADDR_FLAG_LINKLOCAL; + break; + case 's': + case 'S': + naflags |= NI_NODEADDR_FLAG_SITELOCAL; + break; + case 'g': + case 'G': + naflags |= NI_NODEADDR_FLAG_GLOBAL; + break; + case 'A': /* experimental. not in the spec */ +#ifdef NI_NODEADDR_FLAG_ANYCAST + naflags |= NI_NODEADDR_FLAG_ANYCAST; + break; +#else + errx(1, "-a A is not supported on " + "the platform"); + /*NOTREACHED*/ +#endif + default: + usage(); + /*NOTREACHED*/ + } + } + break; + } + case 'b': +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + errno = 0; + e = NULL; + lsockbufsize = strtoul(optarg, &e, 10); + sockbufsize = lsockbufsize; + if (errno || !*optarg || *e || + sockbufsize != lsockbufsize) + errx(1, "invalid socket buffer size"); +#else + errx(1, "-b option ignored: SO_SNDBUF/SO_RCVBUF " + "socket options not supported"); +#endif + break; + case 'B': + boundif = optarg; + break; + case 'C': + nocell++; + break; + case 'c': + npackets = strtol(optarg, &e, 10); + if (npackets <= 0 || *optarg == '\0' || *e != '\0') + errx(1, + "illegal number of packets -- %s", optarg); + break; + case 'D': + options |= F_DONTFRAG; + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'f': + if (getuid()) { + errno = EPERM; + errx(1, "Must be superuser to flood ping"); + } + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + break; + case 'g': + gateway = optarg; + break; + case 'G': { + char *ptr ; + char *tofree; + unsigned long ultmp; + + tofree = strdup(optarg); + if (tofree == NULL) + errx(1, "### strdup() failed"); + ptr = tofree; + do { + char *str; + char *ep; + + if ((str = strsep(&ptr, ",")) == NULL) + errx(1, "-G requires maximum packet size"); + ultmp = strtoul(str, &ep, 0); + if (*ep || ep == optarg) + errx(EX_USAGE, "option -G invalid maximum packet size: `%s'", + str); + options |= F_SWEEP; + sweepmax = ultmp; + if (sweepmax < 1 || sweepmax > MAXDATALEN) { + errx(1, + "-G invalid maximum packet size, needs to be between 1 and %d", + MAXDATALEN); + } + + if ((str = strsep(&ptr, ",")) == NULL) + break; + if (*str != 0) { + ultmp = strtoul(str, &ep, 0); + if (*ep || ep == optarg) + errx(EX_USAGE, "option -G invalid minimum packet size: `%s'", + str); + sweepmin = ultmp; + if (sweepmin < 0 || sweepmin > MAXDATALEN) { + errx(1, + "-G invalid minimum packet size, needs to be between 0 and %d", + MAXDATALEN); + } + } + + if ((str = strsep(&ptr, ",")) == NULL) + break; + if (*str == 0) + break; + ultmp = strtoul(str, &ep, 0); + if (*ep || ep == optarg) + errx(EX_USAGE, "option -G invalid sweep increment size: `%s'", + str); + sweepincr = ultmp; + if (sweepincr < 1 || sweepincr > MAXDATALEN) { + errx(1, + "-G invalid sweep increment size, needs to be between 1 and %d", + MAXDATALEN); + } + } while (0); + free(tofree); + break; + } + case 'H': + options |= F_HOSTNAME; + break; + case 'h': /* hoplimit */ + hoplimit = strtol(optarg, &e, 10); + if (*optarg == '\0' || *e != '\0') + errx(1, "illegal hoplimit %s", optarg); + if (255 < hoplimit || hoplimit < -1) + errx(1, + "illegal hoplimit -- %s", optarg); + break; + case 'I': + ifname = optarg; + options |= F_INTERFACE; +#ifndef USE_SIN6_SCOPE_ID + usepktinfo++; +#endif + break; + case 'i': /* wait between sending packets */ + t = strtod(optarg, &e); + if (*optarg == '\0' || *e != '\0') + errx(1, "illegal timing interval %s", optarg); + if (t < 0.1 && getuid()) { + errx(1, "%s: only root may use interval < 1s", + strerror(EPERM)); + } + intvl.tv_sec = (long)t; + intvl.tv_usec = + (long)((t - intvl.tv_sec) * 1000000); + if (intvl.tv_sec < 0) + errx(1, "illegal timing interval %s", optarg); + /* less than 1/hz does not make sense */ + if (intvl.tv_sec == 0 && intvl.tv_usec < 1) { + warnx("too small interval, raised to .000001"); + intvl.tv_usec = 1; + } + options |= F_INTERVAL; + break; + case 'k': + if (strcasecmp(optarg, "sendmsg") == 0) { + use_sendmsg++; + break; + } + if (strcasecmp(optarg, "recvmsg") == 0) { + use_recvmsg++; + break; + } + so_traffic_class = str2sotc(optarg, &valid); + if (valid == false) + errx(EX_USAGE, "bad traffic class: `%s'", + optarg); + break; + case 'K': + if (strcasecmp(optarg, "sendmsg") == 0) { + use_sendmsg++; + break; + } + net_service_type = str2netservicetype(optarg, &valid); + if (valid == false) + errx(EX_USAGE, "bad network service type: `%s'", + optarg); + /* suppress default traffic class (-k can still be specified after -K) */ + so_traffic_class = -1; + break; + case 'l': + if (getuid()) { + errno = EPERM; + errx(1, "Must be superuser to preload"); + } + preload = strtol(optarg, &e, 10); + if (preload < 0 || *optarg == '\0' || *e != '\0') + errx(1, "illegal preload value -- %s", optarg); + break; + case 'm': +#ifdef IPV6_USE_MIN_MTU + mflag++; + break; +#else + errx(1, "-%c is not supported on this platform", ch); + /*NOTREACHED*/ +#endif + case 'n': + options &= ~F_HOSTNAME; + break; + case 'N': + options |= F_NIGROUP; + nig_oldmcprefix++; + break; + case 'o': + options |= F_ONCE; + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + fill((char *)datap, optarg); + break; + case 'q': + options |= F_QUIET; + break; + case 'r': + options |= F_AUDIBLE; + break; + case 'R': + options |= F_MISSED; + break; + case 'S': + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */ + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_RAW; + hints.ai_protocol = IPPROTO_ICMPV6; + + ret_ga = getaddrinfo(optarg, NULL, &hints, &res); + if (ret_ga) { + errx(1, "invalid source address: %s", + gai_strerror(ret_ga)); + } + /* + * res->ai_family must be AF_INET6 and res->ai_addrlen + * must be sizeof(src). + */ + memcpy(&src, res->ai_addr, res->ai_addrlen); + srclen = res->ai_addrlen; + freeaddrinfo(res); + res = NULL; + options |= F_SRCADDR; + break; + case 's': /* size of packet to send */ + datalen = strtol(optarg, &e, 10); + if (datalen <= 0 || *optarg == '\0' || *e != '\0') + errx(1, "illegal datalen value -- %s", optarg); + if (datalen > MAXDATALEN) { + errx(1, + "datalen value too large, maximum is %d", + MAXDATALEN); + } + break; + case 't': + options &= ~F_NOUSERDATA; + options |= F_SUPTYPES; + break; + case 'v': + options |= F_VERBOSE; + break; + case 'w': + options &= ~F_NOUSERDATA; + options |= F_FQDN; + break; + case 'W': + options &= ~F_NOUSERDATA; + options |= F_FQDNOLD; + break; + case 'z': + tclass = str2tclass(optarg, &valid); + if (valid == false) + errx(1, "illegal TOS value -- %s", optarg); + rcvtclass = 1; + break; + case 'X': + alarmtimeout = strtoul(optarg, &e, 0); + if (alarmtimeout < 1 || alarmtimeout == ULONG_MAX) + errx(EX_USAGE, "invalid timeout: `%s'", + optarg); + if (alarmtimeout > MAXALARM) + errx(EX_USAGE, "invalid timeout: `%s' > %d", + optarg, MAXALARM); + alarm((int)alarmtimeout); + break; +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + case 'P': + options |= F_POLICY; + if (!strncmp("in", optarg, 2)) { + if ((policy_in = strdup(optarg)) == NULL) + errx(1, "strdup"); + } else if (!strncmp("out", optarg, 3)) { + if ((policy_out = strdup(optarg)) == NULL) + errx(1, "strdup"); + } else + errx(1, "invalid security policy"); + break; +#else + case 'A': + options |= F_AUTHHDR; + break; + case 'E': + options |= F_ENCRYPT; + break; +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + case 0: + switch (longopt_flag) { + case LOF_CONNECT: + options |= F_CONNECT; + break; + case LOF_PRTIME: + options |= F_PRTIME; + thiszone = gmt2local(0); + break; + default: + break; + } + longopt_flag = 0; + break; + default: + usage(); + /*NOTREACHED*/ + } + } + + if (boundif != NULL && (ifscope = if_nametoindex(boundif)) == 0) + errx(1, "bad interface name"); + + if ((options & F_SWEEP) && !sweepmax) + errx(EX_USAGE, "Maximum sweep size must be specified"); + + if ((options & F_SWEEP) && (options & F_NOUSERDATA)) + errx(EX_USAGE, "Option -G incompatible with -t, -w and -W"); + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + /*NOTREACHED*/ + } + + if (argc > 1) { +#ifdef IPV6_RECVRTHDR /* 2292bis */ + rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0, + argc - 1)); +#else /* RFC2292 */ + rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1); +#endif + if (rthlen == 0) { + errx(1, "too many intermediate hops"); + /*NOTREACHED*/ + } + ip6optlen += rthlen; + } + + if (options & F_NIGROUP) { + target = nigroup(argv[argc - 1], nig_oldmcprefix); + if (target == NULL) { + usage(); + /*NOTREACHED*/ + } + } else + target = argv[argc - 1]; + + /* getaddrinfo */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_RAW; + hints.ai_protocol = IPPROTO_ICMPV6; + + ret_ga = getaddrinfo(target, NULL, &hints, &res); + if (ret_ga) + errx(1, "getaddrinfo -- %s", gai_strerror(ret_ga)); + if (res->ai_canonname) + hostname = strdup(res->ai_canonname); + else + hostname = target; + + if (!res->ai_addr) + errx(1, "getaddrinfo failed"); + + (void)memcpy(&dst, res->ai_addr, res->ai_addrlen); + + res->ai_socktype = getuid() ? SOCK_DGRAM : SOCK_RAW; + res->ai_protocol = IPPROTO_ICMPV6; + + if ((s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) < 0) + err(1, "socket"); + + if (ifscope != 0) { + if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, + (char *)&ifscope, sizeof (ifscope)) != 0) + err(1, "setsockopt(IPV6_BOUND_IF)"); + } + + if (nocell != 0) { + if (setsockopt(s, IPPROTO_IPV6, IPV6_NO_IFT_CELLULAR, + (char *)&nocell, sizeof (nocell)) != 0) + err(1, "setsockopt(IPV6_NO_IFT_CELLULAR)"); + } + + /* set the source address if specified. */ + if ((options & F_SRCADDR) && + bind(s, (struct sockaddr *)&src, srclen) != 0) { + err(1, "bind"); + } + + /* set the gateway (next hop) if specified */ + if (gateway) { + struct addrinfo ghints, *gres; + int error; + + memset(&ghints, 0, sizeof(ghints)); + ghints.ai_family = AF_INET6; + ghints.ai_socktype = SOCK_RAW; + ghints.ai_protocol = IPPROTO_ICMPV6; + + error = getaddrinfo(gateway, NULL, &hints, &gres); + if (error) { + errx(1, "getaddrinfo for the gateway %s: %s", + gateway, gai_strerror(error)); + } + if (gres->ai_next && (options & F_VERBOSE)) + warnx("gateway resolves to multiple addresses"); + + if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP, + gres->ai_addr, gres->ai_addrlen)) { + err(1, "setsockopt(IPV6_NEXTHOP)"); + } + + freeaddrinfo(gres); + } + + /* + * let the kerel pass extension headers of incoming packets, + * for privileged socket options + */ + if ((options & F_VERBOSE) != 0) { + int opton = 1; + +#ifdef IPV6_RECVHOPOPTS + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVHOPOPTS)"); +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_HOPOPTS)"); +#endif +#ifdef IPV6_RECVDSTOPTS + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVDSTOPTS)"); +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_DSTOPTS)"); +#endif +#ifdef IPV6_RECVRTHDRDSTOPTS + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)"); +#endif + } + + /* revoke root privilege */ + if (seteuid(getuid()) != 0) + err(1, "seteuid() failed"); + if (setuid(getuid()) != 0) + err(1, "setuid() failed"); + + if ((options & F_FLOOD) && (options & F_INTERVAL)) + errx(1, "-f and -i incompatible options"); + + if ((options & F_CONNECT)) { + if (connect(s, (struct sockaddr *)&dst, sizeof(dst)) == -1) + err(EX_OSERR, "connect"); + } + + if (sweepmax) { + if (sweepmin >= sweepmax) + errx(EX_USAGE, "Maximum packet size must be greater than the minimum packet size"); + + if (datalen != DEFDATALEN) + errx(EX_USAGE, "Packet size and ping sweep are mutually exclusive"); + + if (npackets > 0) { + snpackets = npackets; + npackets = 0; + } else + snpackets = 1; + datalen = sweepmin; + } + + if ((options & F_NOUSERDATA) == 0) { + if (datalen >= sizeof(struct tv32)) { + /* we can time transfer */ + timing = 1; + } else + timing = 0; + /* in F_VERBOSE case, we may get non-echoreply packets*/ + if (options & F_VERBOSE) + packlen = MAX(2048, sweepmax) + IP6LEN + ICMP6ECHOLEN + EXTRA; + else + packlen = MAX(datalen, sweepmax) + IP6LEN + ICMP6ECHOLEN + EXTRA; + } else { + /* suppress timing for node information query */ + timing = 0; + datalen = 2048; + packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA; + } + + if (!(packet = (u_char *)malloc(packlen))) + err(1, "Unable to allocate packet"); + if (!(options & F_PINGFILLED)) + for (i = ICMP6ECHOLEN; i < MAX(datalen, sweepmax); ++i) + *datap++ = i; + + ident = getpid() & 0xFFFF; + arc4random_buf(nonce, sizeof(nonce)); + optval = 1; + if (options & F_DONTFRAG) + if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG, + &optval, sizeof(optval)) == -1) + err(1, "IPV6_DONTFRAG"); + hold = 1; + + if (options & F_SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, + sizeof(hold)); + + hold = 1; + (void) setsockopt(s, SOL_SOCKET, SO_RECV_ANYIF, (char *)&hold, + sizeof(hold)); + + optval = IPV6_DEFHLIM; + if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &optval, sizeof(optval)) == -1) + err(1, "IPV6_MULTICAST_HOPS"); +#ifdef IPV6_USE_MIN_MTU + if (mflag != 1) { + optval = mflag > 1 ? 0 : 1; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_USE_MIN_MTU)"); + } +#ifdef IPV6_RECVPATHMTU + else { + optval = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_RECVPATHMTU)"); + } +#endif /* IPV6_RECVPATHMTU */ +#endif /* IPV6_USE_MIN_MTU */ + +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + if (options & F_POLICY) { + if (setpolicy(s, policy_in) < 0) + errx(1, "%s", ipsec_strerror()); + if (setpolicy(s, policy_out) < 0) + errx(1, "%s", ipsec_strerror()); + } +#else + if (options & F_AUTHHDR) { + optval = IPSEC_LEVEL_REQUIRE; +#ifdef IPV6_AUTH_TRANS_LEVEL + if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)"); +#else /* old def */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_AUTH_LEVEL)"); +#endif + } + if (options & F_ENCRYPT) { + optval = IPSEC_LEVEL_REQUIRE; + if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)"); + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif + +#ifdef ICMP6_FILTER + { + struct icmp6_filter filt; + if (!(options & F_VERBOSE)) { + ICMP6_FILTER_SETBLOCKALL(&filt); + if ((options & F_FQDN) || (options & F_FQDNOLD) || + (options & F_NODEADDR) || (options & F_SUPTYPES)) + ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt); + else + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); + } else { + ICMP6_FILTER_SETPASSALL(&filt); + } + if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) + err(1, "setsockopt(ICMP6_FILTER)"); + } +#endif /*ICMP6_FILTER*/ + + /* let the kerel pass extension headers of incoming packets */ + if ((options & F_VERBOSE) != 0) { + int opton = 1; + +#ifdef IPV6_RECVRTHDR + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVRTHDR)"); +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RTHDR)"); +#endif + } + + if (tclass != -2) { + int on = 1; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVTCLASS, &on, + sizeof(on))) + err(1, "setsockopt(IPV6_RECVTCLASS)"); + + if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &tclass, + sizeof(tclass))) + err(1, "setsockopt(IPV6_TCLASS)"); + } + +/* + optval = 1; + if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &optval, sizeof(optval)) == -1) + err(1, "IPV6_MULTICAST_LOOP"); +*/ + + /* Specify the outgoing interface and/or the source address */ + if (usepktinfo) + ip6optlen += CMSG_SPACE(sizeof(struct in6_pktinfo)); + + if (hoplimit != -1) + ip6optlen += CMSG_SPACE(sizeof(int)); + +#ifdef IPV6_USE_MIN_MTU + if (mflag != 1) + ip6optlen += CMSG_SPACE(sizeof(int)); +#endif /* IPV6_USE_MIN_MTU */ + + if (tclass != -2) + ip6optlen += CMSG_SPACE(sizeof(int)); + + if (use_sendmsg == 0) { + if (net_service_type != -1) + if (setsockopt(s, SOL_SOCKET, SO_NET_SERVICE_TYPE, + (void *)&net_service_type, sizeof (net_service_type)) != 0) + warn("setsockopt(SO_NET_SERVICE_TYPE"); + if (so_traffic_class != -1) { + if (setsockopt(s, SOL_SOCKET, SO_TRAFFIC_CLASS, + (void *)&so_traffic_class, sizeof (so_traffic_class)) != 0) + warn("setsockopt(SO_TRAFFIC_CLASS"); + + } + } else { + if (net_service_type != -1) + ip6optlen += CMSG_SPACE(sizeof(int)); + if (so_traffic_class != -1) + ip6optlen += CMSG_SPACE(sizeof(int)); + } + if (use_recvmsg > 0) { + int on = 1; + if (setsockopt(s, SOL_SOCKET, SO_RECV_TRAFFIC_CLASS, + (void *)&on, sizeof (on)) != 0) + warn("setsockopt(SO_RECV_TRAFFIC_CLASS"); + } + + /* set IP6 packet options */ + if (ip6optlen) { + if ((scmsg = (char *)malloc(ip6optlen)) == 0) + errx(1, "can't allocate enough memory"); + smsghdr.msg_control = (caddr_t)scmsg; + smsghdr.msg_controllen = ip6optlen; + scmsgp = (struct cmsghdr *)scmsg; + } + if (usepktinfo) { + pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp)); + memset(pktinfo, 0, sizeof(*pktinfo)); + scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_PKTINFO; + scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); + } + + /* set the outgoing interface */ + if (ifname) { +#ifndef USE_SIN6_SCOPE_ID + /* pktinfo must have already been allocated */ + if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0) + errx(1, "%s: invalid interface name", ifname); +#else + if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0) + errx(1, "%s: invalid interface name", ifname); +#endif + } + if (hoplimit != -1) { + scmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_HOPLIMIT; + *(int *)(CMSG_DATA(scmsgp)) = hoplimit; + + scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); + } + +#ifdef IPV6_USE_MIN_MTU + if (mflag != 1) { + optval = mflag > 1 ? 0 : 1; + + scmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_USE_MIN_MTU; + *(int *)(CMSG_DATA(scmsgp)) = optval; + + scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); + } +#endif /* IPV6_USE_MIN_MTU */ + + if (argc > 1) { /* some intermediate addrs are specified */ + int hops, error; +#ifdef USE_RFC2292BIS + int rthdrlen; +#endif + +#ifdef USE_RFC2292BIS + rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); + scmsgp->cmsg_len = CMSG_LEN(rthdrlen); + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_RTHDR; + rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp); + rthdr = inet6_rth_init((void *)rthdr, rthdrlen, + IPV6_RTHDR_TYPE_0, argc - 1); + if (rthdr == NULL) + errx(1, "can't initialize rthdr"); +#else /* old advanced API */ + if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp, + IPV6_RTHDR_TYPE_0)) == 0) + errx(1, "can't initialize rthdr"); +#endif /* USE_RFC2292BIS */ + + for (hops = 0; hops < argc - 1; hops++) { + struct addrinfo *iaip; + + if ((error = getaddrinfo(argv[hops], NULL, &hints, + &iaip))) + errx(1, "%s", gai_strerror(error)); + if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6) + errx(1, + "bad addr family of an intermediate addr"); + +#ifdef USE_RFC2292BIS + if (inet6_rth_add(rthdr, + &(SIN6(iaip->ai_addr))->sin6_addr)) + errx(1, "can't add an intermediate node"); +#else /* old advanced API */ + if (inet6_rthdr_add(scmsgp, + &(SIN6(iaip->ai_addr))->sin6_addr, + IPV6_RTHDR_LOOSE)) + errx(1, "can't add an intermediate node"); +#endif /* USE_RFC2292BIS */ + freeaddrinfo(iaip); + } + +#ifndef USE_RFC2292BIS + if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE)) + errx(1, "can't set the last flag"); +#endif + + scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); + } + + if (tclass != -2) { + scmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_TCLASS; + *(int *)(CMSG_DATA(scmsgp)) = tclass; + + scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); + } + if (use_sendmsg != 0) { + if (so_traffic_class != -1) { + scmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + scmsgp->cmsg_level = SOL_SOCKET; + scmsgp->cmsg_type = SO_TRAFFIC_CLASS; + *(int *)(CMSG_DATA(scmsgp)) = so_traffic_class; + + scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); + } + if (net_service_type != -1) { + scmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + scmsgp->cmsg_level = SOL_SOCKET; + scmsgp->cmsg_type = SO_NET_SERVICE_TYPE; + *(int *)(CMSG_DATA(scmsgp)) = net_service_type; + } + } + if (!(options & F_SRCADDR)) { + /* + * get the source address. XXX since we revoked the root + * privilege, we cannot use a raw socket for this. + */ + int dummy; + socklen_t len = sizeof(src); + + if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + err(1, "UDP socket"); + + if (ifscope != 0) { + if (setsockopt(dummy, IPPROTO_IPV6, IPV6_BOUND_IF, + (char *)&ifscope, sizeof (ifscope)) != 0) + err(1, "setsockopt(IPV6_BOUND_IF)"); + } + + if (nocell != 0) { + if (setsockopt(dummy, IPPROTO_IPV6, IPV6_NO_IFT_CELLULAR, + (char *)&nocell, sizeof (nocell)) != 0) + err(1, "setsockopt(IPV6_NO_IFT_CELLULAR)"); + } + + src.sin6_family = AF_INET6; + src.sin6_addr = dst.sin6_addr; + src.sin6_port = ntohs(DUMMY_PORT); + src.sin6_scope_id = dst.sin6_scope_id; + +#ifdef USE_RFC2292BIS + if (pktinfo && + setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, + (void *)pktinfo, sizeof(*pktinfo))) + err(1, "UDP setsockopt(IPV6_PKTINFO)"); + + if (hoplimit != -1 && + setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (void *)&hoplimit, sizeof(hoplimit))) + err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)"); + + if (hoplimit != -1 && + setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (void *)&hoplimit, sizeof(hoplimit))) + err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)"); + + if (rthdr && + setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR, + (void *)rthdr, (rthdr->ip6r_len + 1) << 3)) + err(1, "UDP setsockopt(IPV6_RTHDR)"); +#else /* old advanced API */ + if (smsghdr.msg_control && + setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS, + (void *)smsghdr.msg_control, smsghdr.msg_controllen)) + err(1, "UDP setsockopt(IPV6_PKTOPTIONS)"); +#endif + + if (connect(dummy, (struct sockaddr *)&src, len) < 0) + err(1, "UDP connect"); + + if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0) + err(1, "getsockname"); + + close(dummy); + } + +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + if (sockbufsize) { + if (MAX(datalen, sweepmax) > sockbufsize) + warnx("you need -b to increase socket buffer size"); + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize, + sizeof(sockbufsize)) < 0) + err(1, "setsockopt(SO_SNDBUF)"); + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize, + sizeof(sockbufsize)) < 0) + err(1, "setsockopt(SO_RCVBUF)"); + } + else { + if (MAX(datalen, sweepmax) > 8 * 1024) /*XXX*/ + warnx("you need -b to increase socket buffer size"); + /* + * When pinging the broadcast address, you can get a lot of + * answers. Doing something so evil is useful if you are trying + * to stress the ethernet, or just want to fill the arp cache + * to get some stuff for /etc/ethers. + */ + hold = 48 * 1024; + setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, + sizeof(hold)); + } +#endif + + optval = 1; +#ifndef USE_SIN6_SCOPE_ID +#ifdef IPV6_RECVPKTINFO + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, + sizeof(optval)) < 0) + warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */ +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval, + sizeof(optval)) < 0) + warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */ +#endif +#endif /* USE_SIN6_SCOPE_ID */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, + sizeof(optval)) < 0) + warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */ +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval, + sizeof(optval)) < 0) + warn("setsockopt(IPV6_HOPLIMIT, %d, %lu)", + optval, sizeof(optval)); /* XXX err? */ +#endif + + if (sweepmax) + printf("PING6(40+8+[%lu...%lu] bytes) ", + (unsigned long)(sweepmin), + (unsigned long)(sweepmax)); + else + printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()), + (unsigned long)(pingerlen() - 8)); + printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src))); + printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst))); + + if (preload == 0) + pinger(); + else { + if (npackets != 0 && preload > npackets) + preload = npackets; + while (preload--) + pinger(); + } + gettimeofday(&last, NULL); + + /* + * rdar://25829310 + * + * Clear blocked signals inherited from the parent + */ + sigset_t newset; + sigemptyset(&newset); + if (sigprocmask(SIG_SETMASK, &newset, NULL) != 0) + err(EX_OSERR, "sigprocmask(newset)"); + + seenalrm = seenint = 0; +#ifdef SIGINFO + seeninfo = 0; +#endif + + (void)signal(SIGINT, onsignal); +#ifdef SIGINFO + (void)signal(SIGINFO, onsignal); +#endif + if (alarmtimeout > 0) { + (void)signal(SIGALRM, onsignal); + } + + if (options & F_FLOOD) { + intvl.tv_sec = 0; + intvl.tv_usec = 10000; + } else if ((options & F_INTERVAL) == 0) { + intvl.tv_sec = interval / 1000; + intvl.tv_usec = interval % 1000 * 1000; + } + + /* For control (ancillary) data received from recvmsg() */ + cm = (struct cmsghdr *)malloc(CONTROLLEN); + if (cm == NULL) + err(1, "malloc"); + + almost_done = 0; + while (seenint == 0) { + struct timeval now, timeout; + struct msghdr m; + struct iovec iov[2]; + fd_set rfds; + int n; + + /* signal handling */ + if (seenint) + onint(SIGINT); +#ifdef SIGINFO + if (seeninfo) { + summary(); + seeninfo = 0; + continue; + } +#endif + FD_ZERO(&rfds); + FD_SET(s, &rfds); + gettimeofday(&now, NULL); + timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec; + timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec; + while (timeout.tv_usec < 0) { + timeout.tv_usec += 1000000; + timeout.tv_sec--; + } + while (timeout.tv_usec > 1000000) { + timeout.tv_usec -= 1000000; + timeout.tv_sec++; + } + if (timeout.tv_sec < 0) + timeout.tv_sec = timeout.tv_usec = 0; + + n = select(s + 1, &rfds, NULL, NULL, &timeout); + if (n < 0) + continue; /* EINTR */ + if (n == 1) { + m.msg_name = (caddr_t)&from; + m.msg_namelen = sizeof(from); + memset(&iov, 0, sizeof(iov)); + iov[0].iov_base = (caddr_t)packet; + iov[0].iov_len = packlen; + m.msg_iov = iov; + m.msg_iovlen = 1; + memset(cm, 0, CONTROLLEN); + m.msg_control = (void *)cm; + m.msg_controllen = CONTROLLEN; + m.msg_flags = 0; + + cc = recvmsg(s, &m, 0); + if (cc < 0) { + if (errno != EINTR) { + warn("recvmsg"); + sleep(1); + } + continue; + } else if (cc == 0) { + int mtu; + + /* + * receive control messages only. Process the + * exceptions (currently the only possibility is + * a path MTU notification.) + */ + if ((mtu = get_pathmtu(&m)) > 0) { + if ((options & F_VERBOSE) != 0) { + printf("new path MTU (%d) is " + "notified\n", mtu); + } + } + continue; + } else { + /* + * an ICMPv6 message (probably an echoreply) + * arrived. + */ + pr_pack(packet, cc, &m); + } + if (((options & F_ONCE) != 0 && nreceived > 0) || + (npackets > 0 && nreceived >= npackets) || + (sweepmax && datalen > sweepmax)) + break; + } + if (n == 0 || (options & F_FLOOD)) { + if (npackets == 0 || ntransmitted < npackets) + pinger(); + else { + if (almost_done) + break; + almost_done = 1; + /* + * If we're not transmitting any more packets, + * change the timer to wait two round-trip times + * if we've received any packets or (waittime) + * milliseconds if we haven't. + */ + intvl.tv_usec = 0; + if (nreceived) { + intvl.tv_sec = 2 * tmax / 1000; + if (intvl.tv_sec == 0) + intvl.tv_sec = 1; + } else { + intvl.tv_sec = waittime / 1000; + intvl.tv_usec = waittime % 1000 * 1000; + } + } + gettimeofday(&last, NULL); + if (ntransmitted - nreceived - 1 > nmissedmax) { + nmissedmax = ntransmitted - nreceived - 1; + if (options & F_MISSED) + (void)write(STDOUT_FILENO, &BBELL, 1); + } + } + } + sigemptyset(&newset); + if (sigprocmask(SIG_SETMASK, &newset, NULL) != 0) + err(EX_OSERR, "sigprocmask(newset)"); + summary(); + + if (packet != NULL) + free(packet); + + exit(nreceived == 0 ? 2 : 0); +} + +void +onsignal(int sig) +{ + fflush(stdout); + + switch (sig) { + case SIGINT: + case SIGALRM: + seenint++; + break; +#ifdef SIGINFO + case SIGINFO: + seeninfo++; + break; +#endif + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +size_t +pingerlen(void) +{ + size_t l; + + if (options & F_FQDN) + l = ICMP6_NIQLEN + sizeof(dst.sin6_addr); + else if (options & F_FQDNOLD) + l = ICMP6_NIQLEN; + else if (options & F_NODEADDR) + l = ICMP6_NIQLEN + sizeof(dst.sin6_addr); + else if (options & F_SUPTYPES) + l = ICMP6_NIQLEN; + else + l = ICMP6ECHOLEN + datalen; + + return l; +} + +int +pinger(void) +{ + struct icmp6_hdr *icp; + int i, cc; + struct icmp6_nodeinfo *nip; + int seq; + + if (npackets && ntransmitted >= npackets) + return(-1); /* no more transmission */ + + if (sweepmax && sntransmitted == snpackets) { + datalen += sweepincr; + if (datalen > sweepmax) + return(-1); /* no more transmission */ + sntransmitted = 0; + } + + icp = (struct icmp6_hdr *)outpack; + nip = (struct icmp6_nodeinfo *)outpack; + memset(icp, 0, sizeof(*icp)); + icp->icmp6_cksum = 0; + seq = ntransmitted++; + CLR(seq % mx_dup_ck); + + if (options & F_FQDN) { + icp->icmp6_type = ICMP6_NI_QUERY; + icp->icmp6_code = ICMP6_NI_SUBJ_IPV6; + nip->ni_qtype = htons(NI_QTYPE_FQDN); + nip->ni_flags = htons(0); + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + + memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr, + sizeof(dst.sin6_addr)); + cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr); + datalen = 0; + } else if (options & F_FQDNOLD) { + /* packet format in 03 draft - no Subject data on queries */ + icp->icmp6_type = ICMP6_NI_QUERY; + icp->icmp6_code = 0; /* code field is always 0 */ + nip->ni_qtype = htons(NI_QTYPE_FQDN); + nip->ni_flags = htons(0); + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + + cc = ICMP6_NIQLEN; + datalen = 0; + } else if (options & F_NODEADDR) { + icp->icmp6_type = ICMP6_NI_QUERY; + icp->icmp6_code = ICMP6_NI_SUBJ_IPV6; + nip->ni_qtype = htons(NI_QTYPE_NODEADDR); + nip->ni_flags = naflags; + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + + memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr, + sizeof(dst.sin6_addr)); + cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr); + datalen = 0; + } else if (options & F_SUPTYPES) { + icp->icmp6_type = ICMP6_NI_QUERY; + icp->icmp6_code = ICMP6_NI_SUBJ_FQDN; /*empty*/ + nip->ni_qtype = htons(NI_QTYPE_SUPTYPES); + /* we support compressed bitmap */ + nip->ni_flags = NI_SUPTYPE_FLAG_COMPRESS; + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + cc = ICMP6_NIQLEN; + datalen = 0; + } else { + icp->icmp6_type = ICMP6_ECHO_REQUEST; + icp->icmp6_code = 0; + icp->icmp6_id = htons(ident); + icp->icmp6_seq = ntohs(seq); + if (timing) { + struct timeval tv; + struct tv32 *tv32; + (void)gettimeofday(&tv, NULL); + tv32 = (struct tv32 *)&outpack[ICMP6ECHOLEN]; + tv32->tv32_sec = htonl(tv.tv_sec); + tv32->tv32_usec = htonl(tv.tv_usec); + } + cc = ICMP6ECHOLEN + datalen; + } + +#ifdef DIAGNOSTIC + if (pingerlen() != cc) + errx(1, "internal error; length mismatch"); +#endif + + if ((options & F_CONNECT)) { + smsghdr.msg_name = NULL; + smsghdr.msg_namelen = 0; + } else { + smsghdr.msg_name = (caddr_t)&dst; + smsghdr.msg_namelen = sizeof(dst); + } + memset(&smsgiov, 0, sizeof(smsgiov)); + smsgiov[0].iov_base = (caddr_t)outpack; + smsgiov[0].iov_len = cc; + smsghdr.msg_iov = smsgiov; + smsghdr.msg_iovlen = 1; + + i = sendmsg(s, &smsghdr, 0); + + if (i < 0 || i != cc) { + if (i < 0) + warn("sendmsg"); + (void)printf("ping6: wrote %s %d chars, ret=%d\n", + hostname, cc, i); + } + sntransmitted++; + if (!(options & F_QUIET) && options & F_FLOOD) + (void)write(STDOUT_FILENO, &DOT, 1); + + return(0); +} + +int +myechoreply(const struct icmp6_hdr *icp) +{ + if (ntohs(icp->icmp6_id) == ident) + return 1; + else + return 0; +} + +int +mynireply(const struct icmp6_nodeinfo *nip) +{ + if (memcmp(nip->icmp6_ni_nonce + sizeof(u_int16_t), + nonce + sizeof(u_int16_t), + sizeof(nonce) - sizeof(u_int16_t)) == 0) + return 1; + else + return 0; +} + +char * +dnsdecode(const u_char **sp, const u_char *ep, const u_char *base, char *buf, + size_t bufsiz) + /*base for compressed name*/ +{ + int i = 0; + const u_char *cp; + char cresult[NS_MAXDNAME + 1]; + const u_char *comp; + int l; + + cp = *sp; + *buf = '\0'; + + if (cp >= ep) + return NULL; + while (cp < ep) { + i = *cp; + if (i == 0 || cp != *sp) { + if (strlcat((char *)buf, ".", bufsiz) >= bufsiz) + return NULL; /*result overrun*/ + } + if (i == 0) + break; + cp++; + + if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) { + /* DNS compression */ + if (!base) + return NULL; + + comp = base + (i & 0x3f); + if (dnsdecode(&comp, cp, base, cresult, + sizeof(cresult)) == NULL) + return NULL; + if (strlcat(buf, cresult, bufsiz) >= bufsiz) + return NULL; /*result overrun*/ + break; + } else if ((i & 0x3f) == i) { + if (i > ep - cp) + return NULL; /*source overrun*/ + while (i-- > 0 && cp < ep) { + l = snprintf(cresult, sizeof(cresult), + isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff); + if (l >= sizeof(cresult) || l < 0) + return NULL; + if (strlcat(buf, cresult, bufsiz) >= bufsiz) + return NULL; /*result overrun*/ + cp++; + } + } else + return NULL; /*invalid label*/ + } + if (i != 0) + return NULL; /*not terminated*/ + cp++; + *sp = cp; + return buf; +} + +/* + * pr_pack -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +void +pr_pack(u_char *buf, int cc, struct msghdr *mhdr) +{ +#define safeputc(c) printf((isprint((c)) ? "%c" : "\\%03o"), c) + struct icmp6_hdr *icp; + struct icmp6_nodeinfo *ni; + int i; + int hoplim; + struct sockaddr *from; + int fromlen; + u_char *cp = NULL, *dp, *end = buf + cc; + struct in6_pktinfo *pktinfo = NULL; + struct timeval tv, tp; + struct tv32 *tpp; + double triptime = 0; + int dupflag; + size_t off; + int oldfqdn; + u_int16_t seq; + char dnsname[NS_MAXDNAME + 1]; + int tclass = 0; + int sotc = -1; + + (void)gettimeofday(&tv, NULL); + + if (!mhdr || !mhdr->msg_name || + mhdr->msg_namelen != sizeof(struct sockaddr_in6) || + ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) { + if (options & F_VERBOSE) + warnx("invalid peername"); + return; + } + from = (struct sockaddr *)mhdr->msg_name; + fromlen = mhdr->msg_namelen; + if (cc < sizeof(struct icmp6_hdr)) { + if (options & F_VERBOSE) + warnx("packet too short (%d bytes) from %s", cc, + pr_addr(from, fromlen)); + return; + } + if (((mhdr->msg_flags & MSG_CTRUNC) != 0) && + (options & F_VERBOSE) != 0) + warnx("some control data discarded, insufficient buffer size"); + icp = (struct icmp6_hdr *)buf; + ni = (struct icmp6_nodeinfo *)buf; + off = 0; + + if ((hoplim = get_hoplim(mhdr)) == -1) { + warnx("failed to get receiving hop limit"); + return; + } + if ((pktinfo = get_rcvpktinfo(mhdr)) == NULL) { + warnx("failed to get receiving packet information"); + return; + } + if (rcvtclass && (tclass = get_tclass(mhdr)) == -1) { + warnx("failed to get receiving traffic class"); + return; + } + + if (use_recvmsg > 0) + sotc = get_so_traffic_class(mhdr); + + if (icp->icmp6_type == ICMP6_ECHO_REPLY && myechoreply(icp)) { + seq = ntohs(icp->icmp6_seq); + ++nreceived; + if (timing) { + tpp = (struct tv32 *)(icp + 1); + tp.tv_sec = ntohl(tpp->tv32_sec); + tp.tv_usec = ntohl(tpp->tv32_usec); + tvsub(&tv, &tp); + triptime = ((double)tv.tv_sec) * 1000.0 + + ((double)tv.tv_usec) / 1000.0; + tsum += triptime; + tsumsq += triptime * triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } + + if (TST(seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_WAITTIME && triptime > waittime) { + ++nrcvtimeout; + return; + } + + if (options & F_FLOOD) + (void)write(STDOUT_FILENO, &BSPACE, 1); + else { + if (options & F_AUDIBLE) + (void)write(STDOUT_FILENO, &BBELL, 1); + if (options & F_PRTIME) + pr_currenttime(); + (void)printf("%d bytes from %s, icmp_seq=%u", cc, + pr_addr(from, fromlen), seq); + (void)printf(" hlim=%d", hoplim); + if ((options & F_VERBOSE) != 0) { + struct sockaddr_in6 dstsa; + + memset(&dstsa, 0, sizeof(dstsa)); + dstsa.sin6_family = AF_INET6; + dstsa.sin6_len = sizeof(dstsa); + dstsa.sin6_scope_id = pktinfo->ipi6_ifindex; + dstsa.sin6_addr = pktinfo->ipi6_addr; + (void)printf(" dst=%s", + pr_addr((struct sockaddr *)&dstsa, + sizeof(dstsa))); + } + if (timing) + (void)printf(" time=%.3f ms", triptime); + if (dupflag) { + if (!IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) + (void)printf("(DUP!)"); + } + if (rcvtclass) + (void)printf(" tclass=%d", tclass); + if (sotc != -1) + (void)printf(" sotc=%d", sotc); + /* check the data */ + cp = buf + off + ICMP6ECHOLEN + ICMP6ECHOTMLEN; + dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN; + for (i = 8; cp < end; ++i, ++cp, ++dp) { + if (*cp != *dp) { + (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp); + break; + } + } + } + } else if (icp->icmp6_type == ICMP6_NI_REPLY && mynireply(ni)) { + seq = ntohs(*(u_int16_t *)ni->icmp6_ni_nonce); + ++nreceived; + if (TST(seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_PRTIME) + pr_currenttime(); + (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); + + switch (ntohs(ni->ni_code)) { + case ICMP6_NI_SUCCESS: + break; + case ICMP6_NI_REFUSED: + printf("refused, type 0x%x", ntohs(ni->ni_type)); + goto fqdnend; + case ICMP6_NI_UNKNOWN: + printf("unknown, type 0x%x", ntohs(ni->ni_type)); + goto fqdnend; + default: + printf("unknown code 0x%x, type 0x%x", + ntohs(ni->ni_code), ntohs(ni->ni_type)); + goto fqdnend; + } + + switch (ntohs(ni->ni_qtype)) { + case NI_QTYPE_NOOP: + printf("NodeInfo NOOP"); + break; + case NI_QTYPE_SUPTYPES: + pr_suptypes(ni, end - (u_char *)ni); + break; + case NI_QTYPE_NODEADDR: + pr_nodeaddr(ni, end - (u_char *)ni); + break; + case NI_QTYPE_FQDN: + default: /* XXX: for backward compatibility */ + cp = (u_char *)ni + ICMP6_NIRLEN; + if (buf[off + ICMP6_NIRLEN] == + cc - off - ICMP6_NIRLEN - 1) + oldfqdn = 1; + else + oldfqdn = 0; + if (oldfqdn) { + cp++; /* skip length */ + while (cp < end) { + safeputc(*cp & 0xff); + cp++; + } + } else { + i = 0; + while (cp < end) { + if (dnsdecode((const u_char **)&cp, end, + (const u_char *)(ni + 1), dnsname, + sizeof(dnsname)) == NULL) { + printf("???"); + break; + } + /* + * name-lookup special handling for + * truncated name + */ + if (cp + 1 <= end && !*cp && + strlen(dnsname) > 0) { + dnsname[strlen(dnsname) - 1] = '\0'; + cp++; + } + printf("%s%s", i > 0 ? "," : "", + dnsname); + } + } + if (options & F_VERBOSE) { + int32_t ttl; + int comma = 0; + + (void)printf(" ("); /*)*/ + + switch (ni->ni_code) { + case ICMP6_NI_REFUSED: + (void)printf("refused"); + comma++; + break; + case ICMP6_NI_UNKNOWN: + (void)printf("unknown qtype"); + comma++; + break; + } + + if ((end - (u_char *)ni) < ICMP6_NIRLEN) { + /* case of refusion, unknown */ + /*(*/ + putchar(')'); + goto fqdnend; + } + ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]); + if (comma) + printf(","); + if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) { + (void)printf("TTL=%d:meaningless", + (int)ttl); + } else { + if (ttl < 0) { + (void)printf("TTL=%d:invalid", + ttl); + } else + (void)printf("TTL=%d", ttl); + } + comma++; + + if (oldfqdn) { + if (comma) + printf(","); + printf("03 draft"); + comma++; + } else { + cp = (u_char *)ni + ICMP6_NIRLEN; + if (cp == end) { + if (comma) + printf(","); + printf("no name"); + comma++; + } + } + + if (buf[off + ICMP6_NIRLEN] != + cc - off - ICMP6_NIRLEN - 1 && oldfqdn) { + if (comma) + printf(","); + (void)printf("invalid namelen:%d/%lu", + buf[off + ICMP6_NIRLEN], + (u_long)cc - off - ICMP6_NIRLEN - 1); + comma++; + } + /*(*/ + putchar(')'); + } + fqdnend: + ; + } + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE)) + return; + if (options & F_PRTIME) + pr_currenttime(); + (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); + pr_icmph(icp, end); + } + + if (!(options & F_FLOOD)) { + (void)putchar('\n'); + if (options & F_VERBOSE) + pr_exthdrs(mhdr); + (void)fflush(stdout); + } +#undef safeputc +} + +void +pr_exthdrs(struct msghdr *mhdr) +{ + ssize_t bufsize; + void *bufp; + struct cmsghdr *cm; + + bufsize = 0; + bufp = mhdr->msg_control; + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_level != IPPROTO_IPV6) + continue; + + bufsize = CONTROLLEN - ((caddr_t)CMSG_DATA(cm) - (caddr_t)bufp); + if (bufsize <= 0) + continue; + switch (cm->cmsg_type) { + case IPV6_HOPOPTS: + printf(" HbH Options: "); + pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize); + break; + case IPV6_DSTOPTS: +#ifdef IPV6_RTHDRDSTOPTS + case IPV6_RTHDRDSTOPTS: +#endif + printf(" Dst Options: "); + pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize); + break; + case IPV6_RTHDR: + printf(" Routing: "); + pr_rthdr(CMSG_DATA(cm), (size_t)bufsize); + break; + } + } +} + +#ifdef USE_RFC2292BIS +void +pr_ip6opt(void *extbuf, size_t bufsize) +{ + struct ip6_hbh *ext; + int currentlen; + u_int8_t type; + socklen_t extlen, len; + void *databuf; + size_t offset; + u_int16_t value2; + u_int32_t value4; + + ext = (struct ip6_hbh *)extbuf; + extlen = (ext->ip6h_len + 1) * 8; + printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt, + (unsigned int)ext->ip6h_len, (unsigned long)extlen); + + /* + * Bounds checking on the ancillary data buffer: + * subtract the size of a cmsg structure from the buffer size. + */ + if (bufsize < (extlen + CMSG_SPACE(0))) { + extlen = bufsize - CMSG_SPACE(0); + warnx("options truncated, showing only %u (total=%u)", + (unsigned int)(extlen / 8 - 1), + (unsigned int)(ext->ip6h_len)); + } + + currentlen = 0; + while (1) { + currentlen = inet6_opt_next(extbuf, extlen, currentlen, + &type, &len, &databuf); + if (currentlen == -1) + break; + switch (type) { + /* + * Note that inet6_opt_next automatically skips any padding + * optins. + */ + case IP6OPT_JUMBO: + offset = 0; + (void) inet6_opt_get_val(databuf, offset, + &value4, sizeof(value4)); + printf(" Jumbo Payload Opt: Length %u\n", + (u_int32_t)ntohl(value4)); + break; + case IP6OPT_ROUTER_ALERT: + offset = 0; + (void)inet6_opt_get_val(databuf, offset, + &value2, sizeof(value2)); + printf(" Router Alert Opt: Type %u\n", + ntohs(value2)); + break; + default: + printf(" Received Opt %u len %lu\n", + type, (unsigned long)len); + break; + } + } + return; +} +#else /* !USE_RFC2292BIS */ +/* ARGSUSED */ +void +pr_ip6opt(void *extbuf, size_t bufsize __unused) +{ + putchar('\n'); + return; +} +#endif /* USE_RFC2292BIS */ + +#ifdef USE_RFC2292BIS +void +pr_rthdr(void *extbuf, size_t bufsize) +{ + struct in6_addr *in6; + char ntopbuf[INET6_ADDRSTRLEN]; + struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf; + int i, segments, origsegs, rthsize, size0, size1; + + /* print fixed part of the header */ + printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt, + rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type); + if ((segments = inet6_rth_segments(extbuf)) >= 0) { + printf("%d segments, ", segments); + printf("%d left\n", rh->ip6r_segleft); + } else { + printf("segments unknown, "); + printf("%d left\n", rh->ip6r_segleft); + return; + } + + /* + * Bounds checking on the ancillary data buffer. When calculating + * the number of items to show keep in mind: + * - The size of the cmsg structure + * - The size of one segment (the size of a Type 0 routing header) + * - When dividing add a fudge factor of one in case the + * dividend is not evenly divisible by the divisor + */ + rthsize = (rh->ip6r_len + 1) * 8; + if (bufsize < (rthsize + CMSG_SPACE(0))) { + origsegs = segments; + size0 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 0); + size1 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 1); + segments -= (rthsize - (bufsize - CMSG_SPACE(0))) / + (size1 - size0) + 1; + warnx("segments truncated, showing only %d (total=%d)", + segments, origsegs); + } + + for (i = 0; i < segments; i++) { + in6 = inet6_rth_getaddr(extbuf, i); + if (in6 == NULL) + printf(" [%d]<NULL>\n", i); + else { + if (!inet_ntop(AF_INET6, in6, ntopbuf, + sizeof(ntopbuf))) + strlcpy(ntopbuf, "?", sizeof(ntopbuf)); + printf(" [%d]%s\n", i, ntopbuf); + } + } + + return; + +} + +#else /* !USE_RFC2292BIS */ +/* ARGSUSED */ +void +pr_rthdr(void *extbuf, size_t bufsize __unused) +{ + putchar('\n'); + return; +} +#endif /* USE_RFC2292BIS */ + +int +pr_bitrange(u_int32_t v, int soff, int ii) +{ + int off; + int i; + + off = 0; + while (off < 32) { + /* shift till we have 0x01 */ + if ((v & 0x01) == 0) { + if (ii > 1) + printf("-%u", soff + off - 1); + ii = 0; + switch (v & 0x0f) { + case 0x00: + v >>= 4; + off += 4; + continue; + case 0x08: + v >>= 3; + off += 3; + continue; + case 0x04: case 0x0c: + v >>= 2; + off += 2; + continue; + default: + v >>= 1; + off += 1; + continue; + } + } + + /* we have 0x01 with us */ + for (i = 0; i < 32 - off; i++) { + if ((v & (0x01 << i)) == 0) + break; + } + if (!ii) + printf(" %u", soff + off); + ii += i; + v >>= i; off += i; + } + return ii; +} + +void +pr_suptypes(struct icmp6_nodeinfo *ni, size_t nilen) + /* ni->qtype must be SUPTYPES */ +{ + size_t clen; + u_int32_t v; + const u_char *cp, *end; + u_int16_t cur; + struct cbit { + u_int16_t words; /*32bit count*/ + u_int16_t skip; + } cbit = { 0, 0 }; +#define MAXQTYPES (1 << 16) + size_t off; + int b; + + cp = (u_char *)(ni + 1); + end = ((u_char *)ni) + nilen; + cur = 0; + b = 0; + + printf("NodeInfo Supported Qtypes"); + if (options & F_VERBOSE) { + if (ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) + printf(", compressed bitmap"); + else + printf(", raw bitmap"); + } + + while (cp < end) { + clen = (size_t)(end - cp); + if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) == 0) { + if (clen == 0 || clen > MAXQTYPES / 8 || + clen % sizeof(v)) { + printf("???"); + return; + } + } else { + if (clen < sizeof(cbit) || clen % sizeof(v)) + return; + memcpy(&cbit, cp, sizeof(cbit)); + if (sizeof(cbit) + ntohs(cbit.words) * sizeof(v) > + clen) + return; + cp += sizeof(cbit); + clen = ntohs(cbit.words) * sizeof(v); + if (cur + clen * 8 + (u_long)ntohs(cbit.skip) * 32 > + MAXQTYPES) + return; + } + + for (off = 0; off < clen; off += sizeof(v)) { + memcpy(&v, cp + off, sizeof(v)); + v = (u_int32_t)ntohl(v); + b = pr_bitrange(v, (int)(cur + off * 8), b); + } + /* flush the remaining bits */ + b = pr_bitrange(0, (int)(cur + off * 8), b); + + cp += clen; + cur += clen * 8; + if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) != 0) + cur += ntohs(cbit.skip) * 32; + } +} + +void +pr_nodeaddr(struct icmp6_nodeinfo *ni, int nilen) + /* ni->qtype must be NODEADDR */ +{ + u_char *cp = (u_char *)(ni + 1); + char ntop_buf[INET6_ADDRSTRLEN]; + int withttl = 0; + + nilen -= sizeof(struct icmp6_nodeinfo); + + if (options & F_VERBOSE) { + switch (ni->ni_code) { + case ICMP6_NI_REFUSED: + (void)printf("refused"); + break; + case ICMP6_NI_UNKNOWN: + (void)printf("unknown qtype"); + break; + } + if (ni->ni_flags & NI_NODEADDR_FLAG_TRUNCATE) + (void)printf(" truncated"); + } + putchar('\n'); + if (nilen <= 0) + printf(" no address\n"); + + /* + * In icmp-name-lookups 05 and later, TTL of each returned address + * is contained in the resposne. We try to detect the version + * by the length of the data, but note that the detection algorithm + * is incomplete. We assume the latest draft by default. + */ + if (nilen % (sizeof(u_int32_t) + sizeof(struct in6_addr)) == 0) + withttl = 1; + while (nilen > 0) { + u_int32_t ttl = 0; + + if (withttl) { + /* XXX: alignment? */ + ttl = (u_int32_t)ntohl(*(u_int32_t *)cp); + cp += sizeof(u_int32_t); + nilen -= sizeof(u_int32_t); + } + + if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) == + NULL) + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); + printf(" %s", ntop_buf); + if (withttl) { + if (ttl == 0xffffffff) { + /* + * XXX: can this convention be applied to all + * type of TTL (i.e. non-ND TTL)? + */ + printf("(TTL=infty)"); + } + else + printf("(TTL=%u)", ttl); + } + putchar('\n'); + + nilen -= sizeof(struct in6_addr); + cp += sizeof(struct in6_addr); + } +} + +int +get_hoplim(struct msghdr *mhdr) +{ + struct cmsghdr *cm; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_len == 0) + return(-1); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + return(*(int *)CMSG_DATA(cm)); + } + + return(-1); +} + +struct in6_pktinfo * +get_rcvpktinfo(struct msghdr *mhdr) +{ + struct cmsghdr *cm; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_len == 0) + return(NULL); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) + return((struct in6_pktinfo *)CMSG_DATA(cm)); + } + + return(NULL); +} + +int +get_pathmtu(struct msghdr *mhdr) +{ +#ifdef IPV6_RECVPATHMTU + struct cmsghdr *cm; + struct ip6_mtuinfo *mtuctl = NULL; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_len == 0) + return(0); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PATHMTU && + cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) { + mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm); + + /* + * If the notified destination is different from + * the one we are pinging, just ignore the info. + * We check the scope ID only when both notified value + * and our own value have non-0 values, because we may + * have used the default scope zone ID for sending, + * in which case the scope ID value is 0. + */ + if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr, + &dst.sin6_addr) || + (mtuctl->ip6m_addr.sin6_scope_id && + dst.sin6_scope_id && + mtuctl->ip6m_addr.sin6_scope_id != + dst.sin6_scope_id)) { + if ((options & F_VERBOSE) != 0) { + printf("path MTU for %s is notified. " + "(ignored)\n", + pr_addr((struct sockaddr *)&mtuctl->ip6m_addr, + sizeof(mtuctl->ip6m_addr))); + } + return(0); + } + + /* + * Ignore an invalid MTU. XXX: can we just believe + * the kernel check? + */ + if (mtuctl->ip6m_mtu < IPV6_MMTU) + return(0); + + /* notification for our destination. return the MTU. */ + return((int)mtuctl->ip6m_mtu); + } + } +#endif + return(0); +} + +int +get_tclass(mhdr) + struct msghdr *mhdr; +{ + struct cmsghdr *cm; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_len == 0) + return(-1); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_TCLASS && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + return(*(int *)CMSG_DATA(cm)); + } + + return(-1); +} + +int +get_so_traffic_class(struct msghdr *mhdr) +{ + struct cmsghdr *cm; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_len == 0) + return(-1); + + if (cm->cmsg_level == SOL_SOCKET && + cm->cmsg_type == SO_TRAFFIC_CLASS && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + return(*(int *)CMSG_DATA(cm)); + } + + return(-1); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +void +tvsub(struct timeval *out, struct timeval *in) +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * onint -- + * SIGINT handler. + */ +/* ARGSUSED */ +void +onint(int notused __unused) +{ + /* + * When doing reverse DNS lookups, the seenint flag might not + * be noticed for a while. Just exit if we get a second SIGINT. + */ + if ((options & F_HOSTNAME) && seenint != 0) + _exit(nreceived ? 0 : 2); +} + +/* + * summary -- + * Print out statistics. + */ +void +summary(void) +{ + (void)printf("\n--- %s ping6 statistics ---\n", hostname); + (void)printf("%ld packets transmitted, ", ntransmitted); + (void)printf("%ld packets received, ", nreceived); + if (nrepeats) + (void)printf("+%ld duplicates, ", nrepeats); + if (ntransmitted) { + if (nreceived > ntransmitted) + (void)printf("-- somebody's duplicating packets!"); + else + (void)printf("%.1f%% packet loss", + ((((double)ntransmitted - nreceived) * 100.0) / + ntransmitted)); + } + if (nrcvtimeout) + printf(", %ld packets out of wait time", nrcvtimeout); + (void)putchar('\n'); + if (nreceived && timing) { + /* Only display average to microseconds */ + double num = nreceived + nrepeats; + double avg = tsum / num; + double dev = sqrt(tsumsq / num - avg * avg); + (void)printf( + "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n", + tmin, avg, tmax, dev); + (void)fflush(stdout); + } + (void)fflush(stdout); +} + +/*subject type*/ +static const char *niqcode[] = { + "IPv6 address", + "DNS label", /*or empty*/ + "IPv4 address", +}; + +/*result code*/ +static const char *nircode[] = { + "Success", "Refused", "Unknown", +}; + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +void +pr_icmph(struct icmp6_hdr *icp, u_char *end) +{ + char ntop_buf[INET6_ADDRSTRLEN]; + struct nd_redirect *red; + struct icmp6_nodeinfo *ni; + char dnsname[NS_MAXDNAME + 1]; + const u_char *cp; + size_t l; + + switch (icp->icmp6_type) { + case ICMP6_DST_UNREACH: + switch (icp->icmp6_code) { + case ICMP6_DST_UNREACH_NOROUTE: + (void)printf("No Route to Destination\n"); + break; + case ICMP6_DST_UNREACH_ADMIN: + (void)printf("Destination Administratively " + "Unreachable\n"); + break; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + (void)printf("Destination Unreachable Beyond Scope\n"); + break; + case ICMP6_DST_UNREACH_ADDR: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP6_DST_UNREACH_NOPORT: + (void)printf("Destination Port Unreachable\n"); + break; + default: + (void)printf("Destination Unreachable, Bad Code: %d\n", + icp->icmp6_code); + break; + } + /* Print returned IP header information */ + pr_retip((struct ip6_hdr *)(icp + 1), end); + break; + case ICMP6_PACKET_TOO_BIG: + (void)printf("Packet too big mtu = %d\n", + (int)ntohl(icp->icmp6_mtu)); + pr_retip((struct ip6_hdr *)(icp + 1), end); + break; + case ICMP6_TIME_EXCEEDED: + switch (icp->icmp6_code) { + case ICMP6_TIME_EXCEED_TRANSIT: + (void)printf("Time to live exceeded\n"); + break; + case ICMP6_TIME_EXCEED_REASSEMBLY: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d\n", + icp->icmp6_code); + break; + } + pr_retip((struct ip6_hdr *)(icp + 1), end); + break; + case ICMP6_PARAM_PROB: + (void)printf("Parameter problem: "); + switch (icp->icmp6_code) { + case ICMP6_PARAMPROB_HEADER: + (void)printf("Erroneous Header "); + break; + case ICMP6_PARAMPROB_NEXTHEADER: + (void)printf("Unknown Nextheader "); + break; + case ICMP6_PARAMPROB_OPTION: + (void)printf("Unrecognized Option "); + break; + default: + (void)printf("Bad code(%d) ", icp->icmp6_code); + break; + } + (void)printf("pointer = 0x%02x\n", + (u_int32_t)ntohl(icp->icmp6_pptr)); + pr_retip((struct ip6_hdr *)(icp + 1), end); + break; + case ICMP6_ECHO_REQUEST: + (void)printf("Echo Request"); + /* XXX ID + Seq + Data */ + break; + case ICMP6_ECHO_REPLY: + (void)printf("Echo Reply"); + /* XXX ID + Seq + Data */ + break; + case ICMP6_MEMBERSHIP_QUERY: + (void)printf("Listener Query"); + break; + case ICMP6_MEMBERSHIP_REPORT: + (void)printf("Listener Report"); + break; + case ICMP6_MEMBERSHIP_REDUCTION: + (void)printf("Listener Done"); + break; + case ND_ROUTER_SOLICIT: + (void)printf("Router Solicitation"); + break; + case ND_ROUTER_ADVERT: + (void)printf("Router Advertisement"); + break; + case ND_NEIGHBOR_SOLICIT: + (void)printf("Neighbor Solicitation"); + break; + case ND_NEIGHBOR_ADVERT: + (void)printf("Neighbor Advertisement"); + break; + case ND_REDIRECT: + red = (struct nd_redirect *)icp; + (void)printf("Redirect\n"); + if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf, + sizeof(ntop_buf))) + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); + (void)printf("Destination: %s", ntop_buf); + if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf, + sizeof(ntop_buf))) + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); + (void)printf(" New Target: %s", ntop_buf); + break; + case ICMP6_NI_QUERY: + (void)printf("Node Information Query"); + /* XXX ID + Seq + Data */ + ni = (struct icmp6_nodeinfo *)icp; + l = end - (u_char *)(ni + 1); + printf(", "); + switch (ntohs(ni->ni_qtype)) { + case NI_QTYPE_NOOP: + (void)printf("NOOP"); + break; + case NI_QTYPE_SUPTYPES: + (void)printf("Supported qtypes"); + break; + case NI_QTYPE_FQDN: + (void)printf("DNS name"); + break; + case NI_QTYPE_NODEADDR: + (void)printf("nodeaddr"); + break; + case NI_QTYPE_IPV4ADDR: + (void)printf("IPv4 nodeaddr"); + break; + default: + (void)printf("unknown qtype"); + break; + } + if (options & F_VERBOSE) { + switch (ni->ni_code) { + case ICMP6_NI_SUBJ_IPV6: + if (l == sizeof(struct in6_addr) && + inet_ntop(AF_INET6, ni + 1, ntop_buf, + sizeof(ntop_buf)) != NULL) { + (void)printf(", subject=%s(%s)", + niqcode[ni->ni_code], ntop_buf); + } else { +#if 1 + /* backward compat to -W */ + (void)printf(", oldfqdn"); +#else + (void)printf(", invalid"); +#endif + } + break; + case ICMP6_NI_SUBJ_FQDN: + if (end == (u_char *)(ni + 1)) { + (void)printf(", no subject"); + break; + } + printf(", subject=%s", niqcode[ni->ni_code]); + cp = (const u_char *)(ni + 1); + if (dnsdecode(&cp, end, NULL, dnsname, + sizeof(dnsname)) != NULL) + printf("(%s)", dnsname); + else + printf("(invalid)"); + break; + case ICMP6_NI_SUBJ_IPV4: + if (l == sizeof(struct in_addr) && + inet_ntop(AF_INET, ni + 1, ntop_buf, + sizeof(ntop_buf)) != NULL) { + (void)printf(", subject=%s(%s)", + niqcode[ni->ni_code], ntop_buf); + } else + (void)printf(", invalid"); + break; + default: + (void)printf(", invalid"); + break; + } + } + break; + case ICMP6_NI_REPLY: + (void)printf("Node Information Reply"); + /* XXX ID + Seq + Data */ + ni = (struct icmp6_nodeinfo *)icp; + printf(", "); + switch (ntohs(ni->ni_qtype)) { + case NI_QTYPE_NOOP: + (void)printf("NOOP"); + break; + case NI_QTYPE_SUPTYPES: + (void)printf("Supported qtypes"); + break; + case NI_QTYPE_FQDN: + (void)printf("DNS name"); + break; + case NI_QTYPE_NODEADDR: + (void)printf("nodeaddr"); + break; + case NI_QTYPE_IPV4ADDR: + (void)printf("IPv4 nodeaddr"); + break; + default: + (void)printf("unknown qtype"); + break; + } + if (options & F_VERBOSE) { + if (ni->ni_code > sizeof(nircode) / sizeof(nircode[0])) + printf(", invalid"); + else + printf(", %s", nircode[ni->ni_code]); + } + break; + default: + (void)printf("Bad ICMP type: %d", icp->icmp6_type); + } +} + +/* + * pr_iph -- + * Print an IP6 header. + */ +void +pr_iph(struct ip6_hdr *ip6) +{ + u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; + u_int8_t tc; + char ntop_buf[INET6_ADDRSTRLEN]; + + tc = *(&ip6->ip6_vfc + 1); /* XXX */ + tc = (tc >> 4) & 0x0f; + tc |= (ip6->ip6_vfc << 4); + + printf("Vr TC Flow Plen Nxt Hlim\n"); + printf(" %1x %02x %05x %04x %02x %02x\n", + (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow), + ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim); + if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf))) + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); + printf("%s->", ntop_buf); + if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf))) + strlcpy(ntop_buf, "?", sizeof(ntop_buf)); + printf("%s\n", ntop_buf); +} + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +const char * +pr_addr(struct sockaddr *addr, int addrlen) +{ + static char buf[NI_MAXHOST]; + int flag = 0; + + if ((options & F_HOSTNAME) == 0) + flag |= NI_NUMERICHOST; + + if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0) + return (buf); + else + return "?"; +} + +/* + * pr_retip -- + * Dump some info on a returned (via ICMPv6) IPv6 packet. + */ +void +pr_retip(struct ip6_hdr *ip6, u_char *end) +{ + u_char *cp = (u_char *)ip6, nh; + int hlen; + + if (end - (u_char *)ip6 < sizeof(*ip6)) { + printf("IP6"); + goto trunc; + } + pr_iph(ip6); + hlen = sizeof(*ip6); + + nh = ip6->ip6_nxt; + cp += hlen; + while (end - cp >= 8) { + switch (nh) { + case IPPROTO_HOPOPTS: + printf("HBH "); + hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3; + nh = ((struct ip6_hbh *)cp)->ip6h_nxt; + break; + case IPPROTO_DSTOPTS: + printf("DSTOPT "); + hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3; + nh = ((struct ip6_dest *)cp)->ip6d_nxt; + break; + case IPPROTO_FRAGMENT: + printf("FRAG "); + hlen = sizeof(struct ip6_frag); + nh = ((struct ip6_frag *)cp)->ip6f_nxt; + break; + case IPPROTO_ROUTING: + printf("RTHDR "); + hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3; + nh = ((struct ip6_rthdr *)cp)->ip6r_nxt; + break; +#ifdef IPSEC + case IPPROTO_AH: + printf("AH "); + hlen = (((struct ah *)cp)->ah_len+2) << 2; + nh = ((struct ah *)cp)->ah_nxt; + break; +#endif + case IPPROTO_ICMPV6: + printf("ICMP6: type = %d, code = %d\n", + *cp, *(cp + 1)); + return; + case IPPROTO_ESP: + printf("ESP\n"); + return; + case IPPROTO_TCP: + printf("TCP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), + (*(cp + 2) * 256 + *(cp + 3))); + return; + case IPPROTO_UDP: + printf("UDP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), + (*(cp + 2) * 256 + *(cp + 3))); + return; + default: + printf("Unknown Header(%d)\n", nh); + return; + } + + if ((cp += hlen) >= end) + goto trunc; + } + if (end - cp < 8) + goto trunc; + + putchar('\n'); + return; + + trunc: + printf("...\n"); + return; +} + +void +fill(char *bp, char *patp) +{ + int ii, jj, kk; + int pat[16]; + char *cp; + + for (cp = patp; *cp; cp++) + if (!isxdigit(*cp)) + errx(1, "patterns must be specified as hex digits"); + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + +/* xxx */ + if (ii > 0) + for (kk = 0; + kk <= MAXDATALEN - (8 + sizeof(struct tv32) + ii); + kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + if (!(options & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + (void)printf("%02x", bp[jj] & 0xFF); + (void)printf("\n"); + } +} + +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +int +setpolicy(int so __unused, char *policy) +{ + char *buf; + + if (policy == NULL) + return 0; /* ignore */ + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) + errx(1, "%s", ipsec_strerror()); + if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf, + ipsec_get_policylen(buf)) < 0) + warnx("Unable to set IPsec policy"); + free(buf); + + return 0; +} +#endif +#endif + +char * +nigroup(char *name, int nig_oldmcprefix) +{ + char *p; + char *q; + MD5_CTX ctxt; + u_int8_t digest[16]; + u_int8_t c; + size_t l; + char hbuf[NI_MAXHOST]; + struct in6_addr in6; + int valid; + + p = strchr(name, '.'); + if (!p) + p = name + strlen(name); + l = p - name; + if (l > 63 || l > sizeof(hbuf) - 1) + return NULL; /*label too long*/ + strlcpy(hbuf, name, l); + hbuf[(int)l] = '\0'; + + for (q = name; *q; q++) { + if (isupper(*(unsigned char *)q)) + *q = tolower(*(unsigned char *)q); + } + + /* generate 16 bytes of pseudo-random value. */ + memset(&ctxt, 0, sizeof(ctxt)); + MD5Init(&ctxt); + c = l & 0xff; + MD5Update(&ctxt, &c, sizeof(c)); + MD5Update(&ctxt, (unsigned char *)name, l); + MD5Final(digest, &ctxt); + + if (nig_oldmcprefix) { + /* draft-ietf-ipngwg-icmp-name-lookup */ + valid = inet_pton(AF_INET6, "ff02::2:0000:0000", &in6); + } else { + /* RFC 4620 */ + valid = inet_pton(AF_INET6, "ff02::2:ff00:0000", &in6); + } + if (valid != 1) + return NULL; /*XXX*/ + + if (nig_oldmcprefix) { + /* draft-ietf-ipngwg-icmp-name-lookup */ + bcopy(digest, &in6.s6_addr[12], 4); + } else { + /* RFC 4620 */ + bcopy(digest, &in6.s6_addr[13], 3); + } + + if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL) + return NULL; + + return strdup(hbuf); +} + +int +str2sotc(const char *str, bool *valid) +{ + int sotc = -1; + char *endptr; + + *valid = true; + + if (str == NULL || *str == '\0') + *valid = false; + else if (strcasecmp(str, "BK_SYS") == 0) + return SO_TC_BK_SYS; + else if (strcasecmp(str, "BK") == 0) + return SO_TC_BK; + else if (strcasecmp(str, "BE") == 0) + return SO_TC_BE; + else if (strcasecmp(str, "RD") == 0) + return SO_TC_RD; + else if (strcasecmp(str, "OAM") == 0) + return SO_TC_OAM; + else if (strcasecmp(str, "AV") == 0) + return SO_TC_AV; + else if (strcasecmp(str, "RV") == 0) + return SO_TC_RV; + else if (strcasecmp(str, "VI") == 0) + return SO_TC_VI; + else if (strcasecmp(str, "VO") == 0) + return SO_TC_VO; + else if (strcasecmp(str, "CTL") == 0) + return SO_TC_CTL; + else { + sotc = (int)strtol(str, &endptr, 0); + if (*endptr != '\0') + *valid = false; + } + return (sotc); +} + +int +str2netservicetype(const char *str, bool *valid) +{ + int svc = -1; + char *endptr; + + *valid = true; + + if (str == NULL || *str == '\0') + *valid = false; + else if (strcasecmp(str, "BK") == 0) + return NET_SERVICE_TYPE_BK; + else if (strcasecmp(str, "BE") == 0) + return NET_SERVICE_TYPE_BE; + else if (strcasecmp(str, "VI") == 0) + return NET_SERVICE_TYPE_VI; + else if (strcasecmp(str, "SIG") == 0) + return NET_SERVICE_TYPE_SIG; + else if (strcasecmp(str, "VO") == 0) + return NET_SERVICE_TYPE_VO; + else if (strcasecmp(str, "RV") == 0) + return NET_SERVICE_TYPE_RV; + else if (strcasecmp(str, "AV") == 0) + return NET_SERVICE_TYPE_AV; + else if (strcasecmp(str, "OAM") == 0) + return NET_SERVICE_TYPE_OAM; + else if (strcasecmp(str, "RD") == 0) + return NET_SERVICE_TYPE_RD; + else { + svc = (int)strtol(str, &endptr, 0); + if (*endptr != '\0') + *valid = false; + } + return (svc); +} + +u_int8_t +str2tclass(const char *str, bool *valid) +{ + u_int8_t dscp = -1; + char *endptr; + + *valid = true; + + if (str == NULL || *str == '\0') + *valid = false; + else if (strcasecmp(str, "DF") == 0) + dscp = _DSCP_DF; + else if (strcasecmp(str, "EF") == 0) + dscp = _DSCP_EF; + else if (strcasecmp(str, "VA") == 0) + dscp = _DSCP_VA; + + else if (strcasecmp(str, "CS0") == 0) + dscp = _DSCP_CS0; + else if (strcasecmp(str, "CS1") == 0) + dscp = _DSCP_CS1; + else if (strcasecmp(str, "CS2") == 0) + dscp = _DSCP_CS2; + else if (strcasecmp(str, "CS3") == 0) + dscp = _DSCP_CS3; + else if (strcasecmp(str, "CS4") == 0) + dscp = _DSCP_CS4; + else if (strcasecmp(str, "CS5") == 0) + dscp = _DSCP_CS5; + else if (strcasecmp(str, "CS6") == 0) + dscp = _DSCP_CS6; + else if (strcasecmp(str, "CS7") == 0) + dscp = _DSCP_CS7; + + else if (strcasecmp(str, "AF11") == 0) + dscp = _DSCP_AF11; + else if (strcasecmp(str, "AF12") == 0) + dscp = _DSCP_AF12; + else if (strcasecmp(str, "AF13") == 0) + dscp = _DSCP_AF13; + else if (strcasecmp(str, "AF21") == 0) + dscp = _DSCP_AF21; + else if (strcasecmp(str, "AF22") == 0) + dscp = _DSCP_AF22; + else if (strcasecmp(str, "AF23") == 0) + dscp = _DSCP_AF23; + else if (strcasecmp(str, "AF31") == 0) + dscp = _DSCP_AF31; + else if (strcasecmp(str, "AF32") == 0) + dscp = _DSCP_AF32; + else if (strcasecmp(str, "AF33") == 0) + dscp = _DSCP_AF33; + else if (strcasecmp(str, "AF41") == 0) + dscp = _DSCP_AF41; + else if (strcasecmp(str, "AF42") == 0) + dscp = _DSCP_AF42; + else if (strcasecmp(str, "AF43") == 0) + dscp = _DSCP_AF43; + + else { + unsigned long val = strtoul(str, &endptr, 0); + if (*endptr != '\0' || val > 255) + *valid = false; + else + return ((u_int8_t)val); + } + /* DSCP occupies the 6 upper bits of the traffic class field */ + return (dscp << 2); +} + +void +pr_currenttime(void) +{ + int s; + struct timeval tv; + + gettimeofday(&tv, NULL); + + s = (tv.tv_sec + thiszone) % 86400; + printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, + (u_int32_t)tv.tv_usec); +} + +void +usage(void) +{ + (void)fprintf(stderr, +#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC) + "A" +#endif + "usage: ping6 [-" + "Dd" +#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC) + "E" +#endif + "fH" +#ifdef IPV6_USE_MIN_MTU + "m" +#endif + "nNoqrRtvwW] " + "[-a addrtype] [-b bufsiz] [-c count]\n" + " [-g gateway] [-h hoplimit] [-I interface] [-i wait] [-l preload]" +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + " [-P policy]" +#endif + "\n" + " [-p pattern] [-S sourceaddr] [-s packetsize] [-z tclass] " + "[-k traffic_class] [-K net_service_type] " + "[hops ...] host\n"); + (void)fprintf(stderr, "Apple specific options (to be specified before hops or host like all options)\n"); + (void)fprintf(stderr, " -b boundif # bind the socket to the interface\n"); + (void)fprintf(stderr, " -k traffic_class # set traffic class socket option\n"); + (void)fprintf(stderr, " -K net_service_type # set traffic class socket options\n"); + (void)fprintf(stderr, " -apple-connect # call connect(2) in the socket\n"); + (void)fprintf(stderr, " -apple-time # display current time\n"); + (void)fprintf(stderr, " -apple-progress # show progress for debugging\n"); + exit(1); +} diff --git a/network_cmds/pktapctl/pktapctl.8 b/network_cmds/pktapctl/pktapctl.8 new file mode 100644 index 0000000..5f11615 --- /dev/null +++ b/network_cmds/pktapctl/pktapctl.8 @@ -0,0 +1,41 @@ +.Dd 9/28/12
+.Dt pktapctl 8
+.Os Darwin
+.Sh NAME
+.Nm pktapctl
+.Sh SYNOPSIS
+.Nm
+.Op Fl g
+.Op Fl h
+.Op Fl i Ar interface
+.Op Fl p Ar rule
+.Op Fl s Ar rule
+.Sh DESCRIPTION
+.Nm
+let you configure a pktap virtual interface.
+.Pp
+This is an experimental command whose syntax and functionalities may change in
+future releases.
+.Pp
+The options have the following meaning:
+.Bl -tag -width -indent
+.It Fl g
+Get the list of filter rules for the given
+.Ar interface.
+.It Fl h
+Display a quick help and exit.
+.It Fl p
+To add a pass interface filter rule.
+.It Fl s
+To add a skip interface filter rule.
+.El
+.Pp
+Here is the syntax of the interface filter rules:
+.Bl -tag -indent
+.It type number
+An interface type number or 0 for any interface type.
+.It name string
+To specify interface with name matching the given string.
+.El
+.Sh SEE ALSO
+.Xr tcpdump 1
diff --git a/network_cmds/pktapctl/pktapctl.c b/network_cmds/pktapctl/pktapctl.c new file mode 100644 index 0000000..9bbe0ed --- /dev/null +++ b/network_cmds/pktapctl/pktapctl.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/ioctl.h> +#include <net/pktap.h> +#include <stdio.h> +#include <unistd.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> + +const char *ifname = NULL; +unsigned int ifindex = 0; +int do_get = 0; +int num_filter_entries = 0; +struct pktap_filter set_filter[PKTAP_MAX_FILTERS]; + +static const char *parameters_format = " %-24s %s\n"; + +static void +usage(const char *s) +{ + printf("# usage: %s -i <ifname> -g -p <filter_rule> -s <filter_rule> -h\n", s); + printf(" Get or set filtering rules on a pktap interface\n"); + printf(" Options:\n"); + printf(parameters_format, "-h", "display this help"); + printf(parameters_format, "-i <ifname>", "name pktap interface"); + printf(parameters_format, "-g", "get filter rules"); + printf(parameters_format, "-p <filter_rule> param", "add a pass rule"); + printf(parameters_format, "-s <filter_rule> param", "add a skip rule"); + printf(" Format of <filter_rule> parameter:\n"); + printf(parameters_format, "type <iftype>", "interfaces of given type"); + printf(parameters_format, "", "use 0 for any interface type"); + printf(parameters_format, "name <ifname>", "interface of given name"); +} + +static void +print_filter_entry(struct pktap_filter *filter) +{ + printf("filter_op: %u filter_param %u ", filter->filter_op, filter->filter_param); + if (filter->filter_param == PKTAP_FILTER_PARAM_IF_TYPE) + printf("%u", filter->filter_param_if_type); + else if (filter->filter_param == PKTAP_FILTER_PARAM_IF_NAME) + printf("%s", filter->filter_param_if_name); +} + +int main(int argc, char * const argv[]) +{ + int ch; + struct ifdrv ifdr; + int fd = -1; + int i; + + //printf("sizeof(struct pktap_filter) %lu\n", sizeof(struct pktap_filter)); + //printf("sizeof(pktap_filter) %lu\n", sizeof(set_filter)); + + while ((ch = getopt(argc, argv, "ghi:p:s:")) != -1) { + switch (ch) { + case 'g': + do_get++; + break; + + case 'h': + usage(argv[0]); + exit(0); + /* NOT REACHED */ + + case 'i': + ifname = optarg; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + err(1, "if_nametoindex(%s) failed", ifname); + + break; + + case 'p': + case 's': { + /* -p (type|name) <value> */ + struct pktap_filter entry; + + if (num_filter_entries >= PKTAP_MAX_FILTERS) + errx(1, "Too many filter entries, max is %u", PKTAP_MAX_FILTERS); + if (optind + 1 > argc) + errx(1, "-%c needs two arguments optind %d argc %d", ch, optind, argc); + if (ch == 'p') + entry.filter_op = PKTAP_FILTER_OP_PASS; + else + entry.filter_op = PKTAP_FILTER_OP_SKIP; + if (strcmp(optarg, "type") == 0) { + entry.filter_param = PKTAP_FILTER_PARAM_IF_TYPE; + entry.filter_param_if_type = (uint32_t)strtoul(argv[optind], NULL, 0); + } else if (strcmp(optarg, "name") == 0) { + entry.filter_param = PKTAP_FILTER_PARAM_IF_NAME; + snprintf(entry.filter_param_if_name, sizeof(entry.filter_param_if_name), "%s", argv[optind]); + } else + errx(1, "syntax error -p %s", optarg); + printf("Addin entry: "); + print_filter_entry(&entry); + printf("\n"); + set_filter[num_filter_entries] = entry; + + num_filter_entries++; + optind++; + break; + } + + case '?': + default: + err(1, "syntax error"); + exit(0); + /* NOT REACHED */ + } + } + if (ifname == NULL) + errx(1, "missing interface"); + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd == -1) + err(1, "socket(PF_INET, SOCK_DGRAM, 0)"); + + if (num_filter_entries > 0) { + for (i = num_filter_entries; i < PKTAP_MAX_FILTERS; i++) { + struct pktap_filter *filter = set_filter + i; + filter->filter_op = PKTAP_FILTER_OP_NONE; + filter->filter_param = PKTAP_FILTER_PARAM_NONE; + } + + snprintf(ifdr.ifd_name, sizeof(ifdr.ifd_name), "%s", ifname); + ifdr.ifd_cmd = PKTP_CMD_FILTER_SET; + ifdr.ifd_len = sizeof(set_filter); + ifdr.ifd_data = &set_filter[0]; + + if (ioctl(fd, SIOCSDRVSPEC, &ifdr) == -1) + err(1, "ioctl(SIOCSDRVSPEC)"); + + } + + if (do_get) { + struct pktap_filter get_filter[PKTAP_MAX_FILTERS]; + + snprintf(ifdr.ifd_name, sizeof(ifdr.ifd_name), "%s", ifname); + ifdr.ifd_cmd = PKTP_CMD_FILTER_GET; + ifdr.ifd_len = sizeof(get_filter); + ifdr.ifd_data = &get_filter[0]; + + if (ioctl(fd, SIOCGDRVSPEC, &ifdr) == -1) + err(1, "ioctl(SIOCGDRVSPEC)"); + + for (i = 0; i < PKTAP_MAX_FILTERS; i++) { + struct pktap_filter *filter = get_filter + i; + + printf("[%d] ", i); + print_filter_entry(filter); + printf("\n"); + } + } + + return 0; +} + diff --git a/network_cmds/pktmnglr/packet_mangler.c b/network_cmds/pktmnglr/packet_mangler.c new file mode 100644 index 0000000..cf94cd4 --- /dev/null +++ b/network_cmds/pktmnglr/packet_mangler.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// +// Created by Prabhakar Lakhera on 06/23/14. +// + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <sys/errno.h> +#include <sys/sys_domain.h> +#include <sys/ioctl.h> +#include <sys/kern_control.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <stdio.h> +#include <err.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <sysexits.h> +#include <net/packet_mangler.h> + + +#define BUF_MAX 1000 +int doit(); + +Pkt_Mnglr_Flow dir = INOUT; +struct addrinfo * p_localaddr = NULL; +struct addrinfo * p_remoteaddr = NULL; +struct sockaddr_storage l_saddr = {0}; +struct sockaddr_storage r_saddr = {0}; + +int sf = -1; +uint32_t duration = 0; +uint32_t protocol = 0; +uint32_t proto_act_mask = 0; +uint32_t ip_act_mask = 0; +uint16_t local_port = 0; +uint16_t remote_port = 0; +uint8_t activate = 1; + +static const char * +basename(const char * str) +{ + const char *last_slash = strrchr(str, '/'); + + if (last_slash == NULL) + return (str); + else + return (last_slash + 1); +} + +struct option_desc { + const char *option; + const char *description; + int required; +}; + +struct option_desc option_desc_list[] = { + { "-h ", "Help", 0 }, + { "-f flow", "flow direction to apply mangler on. Values can be: in/out/inout. default is inout", 0 }, + { "-l IP address ", "Local IP we are interested in ", 0 }, + { "-r IP address ", "Remote IP we are interested in", 0 }, + { "-m IP action mask ", "IP action mask", 0 }, + { "-t time", "Run duration for which packet mangler will run. A value of 0 means forever (till program is killed).", 0 }, + { "-p IP Protocol ", "IP protocol i.e. one of tcp, udp, icmp, icmpv6", 0 }, + { "-L Local port ", "Local port", 0 }, + { "-R Remote port ", "Remote port", 0 }, + { "-M Protocol action mask ", "Protocol action mask", 0 }, + { NULL, NULL, 0 } /* Mark end of list */ +}; + + +static void +usage(const char *cmd) +{ + struct option_desc *option_desc; + char * usage_str = (char *)malloc(BUF_MAX); + size_t usage_len; + + if (usage_str == NULL) + err(1, "%s: malloc(%d)", __func__, BUF_MAX); + + usage_len = snprintf(usage_str, BUF_MAX, "# usage: %s ", basename(cmd)); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + int len; + + if (option_desc->required) + len = snprintf(usage_str + usage_len, BUF_MAX - usage_len, "%s ", option_desc->option); + else + len = snprintf(usage_str + usage_len, BUF_MAX - usage_len, "[%s] ", option_desc->option); + if (len < 0) + err(1, "%s: snprintf(", __func__); + + usage_len += len; + if (usage_len > BUF_MAX) + break; + } + printf("%s\n", usage_str); + printf("options:\n"); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + printf(" %-20s # %s\n", option_desc->option, option_desc->description); + } + +} + +int +main(int argc, char * const argv[]) { + int ch; + int error; + + if (argc == 1) { + usage(argv[0]); + exit(0); + } + + while ((ch = getopt(argc, argv, "hf:l:r:t:p:m:M:L:R:")) != -1) { + switch (ch) { + case 'h': + usage(argv[0]); + exit(0); + break; + case 'f': { + if (strcasecmp(optarg, "in") == 0) { + dir = IN; + } else if (strcasecmp(optarg, "out") == 0) { + dir = OUT; + } else if (strcasecmp(optarg, "inout") == 0) { + dir = INOUT; + } else { + usage(argv[0]); + errx(1, "syntax error"); + } + } + break; + case 'l': + if ((error = getaddrinfo(optarg, NULL, NULL, &p_localaddr))) + errx(1, "getaddrinfo returned error: %s", gai_strerror(error)); + + break; + case 'r': + if ((error = getaddrinfo(optarg, NULL, NULL, &p_remoteaddr))) + errx(1, "getaddrinfo returned error: %s", gai_strerror(error)); + + break; + case 'm': + ip_act_mask = (uint32_t)atoi(optarg); + break; + case 't': + duration = (uint32_t)atoi(optarg); + break; + case 'p': + /* Only support tcp for now */ + if (strcasecmp(optarg, "tcp") == 0) { + protocol = IPPROTO_TCP; + } else if (strcasecmp(optarg, "udp") == 0) { + protocol = IPPROTO_UDP; + errx(1, "Protocol not supported."); + } else if (strcasecmp(optarg, "icmp") == 0) { + protocol = IPPROTO_ICMP; + errx(1, "Protocol not supported."); + } else if (strcasecmp(optarg, "icmpv6") == 0) { + protocol = IPPROTO_ICMPV6; + errx(1, "Protocol not supported."); + } else { + errx(1, "Protocol not supported."); + } + break; + + case 'L': + local_port = htons((uint16_t)atoi(optarg)); + break; + case 'R': + remote_port = htons((uint16_t)atoi(optarg)); + break; + case 'M': + proto_act_mask = (uint32_t)atoi(optarg); + break; + + default: + warnx("# syntax error, unknow option '%d'", ch); + usage(argv[0]); + exit(0); + } + } + + if (p_localaddr && p_remoteaddr) { + if (p_localaddr->ai_family!=p_remoteaddr->ai_family) { + errx(1, "The address families for local and remote address" + " when both present, must be equal"); + } + } + + + doit(); + + return (0); +} + + +int +doit() +{ + struct sockaddr_ctl addr; + + sf = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (sf == -1) { + err(1, "socket()"); + } + + /* Connect the socket */ + bzero(&addr, sizeof(addr)); + addr.sc_len = sizeof(addr); + addr.sc_family = AF_SYSTEM; + addr.ss_sysaddr = AF_SYS_CONTROL; + + { + struct ctl_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.ctl_name, PACKET_MANGLER_CONTROL_NAME, sizeof(info.ctl_name)); + if (ioctl(sf, CTLIOCGINFO, &info)) { + perror("Could not get ID for kernel control.\n"); + exit(-1); + } + addr.sc_id = info.ctl_id; + addr.sc_unit = 1; + } + + if (connect(sf, (struct sockaddr *)&addr, sizeof(struct sockaddr_ctl)) == -1) { + err(1, "connect()"); + } + + if (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_DIRECTION, + &dir, sizeof(uint32_t)) == -1) { + err(1, "setsockopt could not set direction."); + } + + /* Set the IP addresses for the flow */ + if (p_localaddr) { + l_saddr = *((struct sockaddr_storage *)(p_localaddr->ai_addr)); + + if (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_LOCAL_IP, + &l_saddr, sizeof(struct sockaddr_storage)) == -1) { + err(1, "setsockopt could not set local address."); + } + freeaddrinfo(p_localaddr); + p_localaddr = NULL; + } + + if (p_remoteaddr) { + r_saddr = *((struct sockaddr_storage *)(p_remoteaddr->ai_addr)); + + if (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_REMOTE_IP, + &r_saddr, sizeof(struct sockaddr_storage)) == -1) { + err(1, "setsockopt could not set remote address."); + } + freeaddrinfo(p_remoteaddr); + p_remoteaddr = NULL; + } + + /* Set ports for the flow */ + if (local_port && (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_LOCAL_PORT, + &local_port, sizeof(uint16_t)) == -1)) { + err(1, "setsockopt could not set local port."); + + } + + if (remote_port && (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_REMOTE_PORT, + &remote_port, sizeof(uint16_t)) == -1)) { + err(1, "setsockopt could not set remote port."); + + } + + if (protocol && setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_PROTOCOL, + &protocol, sizeof(uint32_t)) == -1) { + err(1, "setsockopt could not set protocol."); + } + + if (proto_act_mask && + (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_PROTO_ACT_MASK, + &proto_act_mask, sizeof(uint32_t))==-1)) { + err(1, "setsockopt could not set protocol action mask."); + } + + if (setsockopt(sf, SYSPROTO_CONTROL, PKT_MNGLR_OPT_ACTIVATE, + &activate, sizeof(uint8_t))== -1) { + err(1, "setsockopt could not activate packet mangler."); + } + + if (!duration) { + pause(); + } else { + sleep(duration); + } + + close(sf); + return 0; +} diff --git a/network_cmds/rarpd.tproj/rarpd.8 b/network_cmds/rarpd.tproj/rarpd.8 new file mode 100644 index 0000000..39ecff6 --- /dev/null +++ b/network_cmds/rarpd.tproj/rarpd.8 @@ -0,0 +1,92 @@ +.\" +.\" Copyright (c) 1988-1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that: (1) source code distributions +.\" retain the above copyright notice and this paragraph in its entirety, (2) +.\" distributions including binary code include the above copyright notice and +.\" this paragraph in its entirety in the documentation or other materials +.\" provided with the distribution, and (3) all advertising materials mentioning +.\" features or use of this software display the following acknowledgement: +.\" ``This product includes software developed by the University of California, +.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +.\" the University nor the names of its contributors may be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" @(#) $Id: rarpd.8,v 1.1 1999/05/02 03:57:59 wsanchez Exp $ +.\" +.Dd October 26, 1990 +.Dt RARPD 8 +.Sh NAME +.Nm rarpd +.Nd Reverse ARP Daemon +.Sh SYNOPSIS +.Nm rarpd +.Op Fl adf +.Op Ar interface +.Sh DESCRIPTION +.Nm Rarpd +services Reverse ARP requests on the Ethernet connected to +.Ar interface. +Upon receiving a request, +.Nm rarpd +maps the target hardware address to an IP address via its name, which +must be present in both the +.Xr ethers 5 +and +.Xr hosts 5 +databases. +If a host does not exist in both databases, the translation cannot +proceed and a reply will not be sent. +.Pp +Additionally, a request is honored only if the server +(i.e., the host that rarpd is running on) +can "boot" the target; that is, if the directory +.Pa /tftpboot/ Ns Em ipaddr +exists, where +.Rm ipaddr +is the target IP address. +.Pp +In normal operation, +.Nm rarpd +forks a copy of itself and runs in +the background. Anomalies and errors are reported via +.Xr syslog 3 . +.Sh OPTIONS +.Bl -tag -width indent +.It Fl a +Listen on all the Ethernets attached to the system. +If +.Sq Fl a +is omitted, an interface must be specified. +.It Fl d +Run in debug mode, with all the output to stderr. +This option implies the +.Fl f +option. +.It Fl f +Run in the foreground. +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/ethers +.It Pa /etc/hosts +.It Pa /tftpboot +.El +.Sh SEE ALSO +.Xr bpf 4 , +.Rs +.%R A Reverse Address Resolution Protocol +.%N RFC 903 +.%A Finlayson, R. +.%A Mann, T. +.%A Mogul, J.C. +.%A Theimer, M. +.Re +.Sh AUTHORS +Craig Leres (leres@ee.lbl.gov) and Steven McCanne (mccanne@ee.lbl.gov). +Lawrence Berkeley Laboratory, University of California, Berkeley, CA. diff --git a/network_cmds/rarpd.tproj/rarpd.c b/network_cmds/rarpd.tproj/rarpd.c new file mode 100644 index 0000000..1a3ae51 --- /dev/null +++ b/network_cmds/rarpd.tproj/rarpd.c @@ -0,0 +1,830 @@ +/* + * Copyright (c) 1999-2009 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused char copyright[] = +"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* + * rarpd - Reverse ARP Daemon + * + * Usage: rarpd -a [ -d -f ] + * rarpd [ -d -f ] interface + */ + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/time.h> +#include <net/bpf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <dirent.h> + +#define FATAL 1 /* fatal error occurred */ +#define NONFATAL 0 /* non fatal error occurred */ + +/* + * The structure for each interface. + */ +struct if_info { + int ii_fd; /* BPF file descriptor */ + u_char ii_eaddr[6]; /* Ethernet address of this interface */ + in_addr_t ii_ipaddr; /* IP address of this interface */ + in_addr_t ii_netmask; /* subnet or net mask */ + struct if_info *ii_next; +}; +/* + * The list of all interfaces that are being listened to. rarp_loop() + * "selects" on the descriptors in this list. + */ +struct if_info *iflist; + +int rarp_open __P((char *)); +int rarp_bootable __P((in_addr_t)); +void init_one __P((char *)); +void init_all __P((void)); +void rarp_loop __P((void)); +void lookup_eaddr __P((char *, u_char *)); +void lookup_ipaddr __P((char *, in_addr_t *, in_addr_t *)); +void usage __P((void)); +void rarp_process __P((struct if_info *, u_char *)); +void rarp_reply __P((struct if_info *, struct ether_header *, in_addr_t)); +void update_arptab __P((u_char *, in_addr_t)); +void err __P((int, const char *,...)); +void debug __P((const char *,...)); +in_addr_t ipaddrtonetmask __P((in_addr_t)); + +int aflag = 0; /* listen on "all" interfaces */ +int dflag = 0; /* print debugging messages */ +int fflag = 0; /* don't fork */ + +int +main(argc, argv) + int argc; + char **argv; +{ + int op, pid, devnull, f; + char *ifname, *hostname, *name; + + extern int optind, opterr; + + if ((name = strrchr(argv[0], '/')) != NULL) + ++name; + else + name = argv[0]; + if (*name == '-') + ++name; + + /* All error reporting is done through syslogs. */ + openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON); + + opterr = 0; + while ((op = getopt(argc, argv, "adf")) != EOF) { + switch (op) { + case 'a': + ++aflag; + break; + + case 'd': + ++dflag; + break; + + case 'f': + ++fflag; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + ifname = argv[optind++]; + hostname = ifname ? argv[optind] : 0; + if ((aflag && ifname) || (!aflag && ifname == 0)) + usage(); + + if (aflag) + init_all(); + else + init_one(ifname); + + if ((!fflag) && (!dflag)) { + pid = fork(); + if (pid > 0) + /* Parent exits, leaving child in background. */ + exit(0); + else + if (pid == -1) { + err(FATAL, "cannot fork"); + /* NOTREACHED */ + } + /* Fade into the background */ + f = open("/dev/tty", O_RDWR); + if (f >= 0) { + if (ioctl(f, TIOCNOTTY, 0) < 0) { + err(FATAL, "TIOCNOTTY: %s", strerror(errno)); + /* NOTREACHED */ + } + (void) close(f); + } + (void) chdir("/"); + (void) setpgid(0, getpid()); + devnull = open("/dev/null", O_RDWR); + if (devnull >= 0) { + (void) dup2(devnull, 0); + (void) dup2(devnull, 1); + (void) dup2(devnull, 2); + if (devnull > 2) + (void) close(devnull); + } + } + rarp_loop(); + /* NOTREACHED */ + return 0; +} +/* + * Add 'ifname' to the interface list. Lookup its IP address and network + * mask and Ethernet address, and open a BPF file for it. + */ +void +init_one(ifname) + char *ifname; +{ + struct if_info *p; + + p = (struct if_info *)malloc(sizeof(*p)); + if (p == 0) { + err(FATAL, "malloc: %s", strerror(errno)); + /* NOTREACHED */ + } + p->ii_next = iflist; + iflist = p; + + p->ii_fd = rarp_open(ifname); + lookup_eaddr(ifname, p->ii_eaddr); + lookup_ipaddr(ifname, &p->ii_ipaddr, &p->ii_netmask); +} +/* + * Initialize all "candidate" interfaces that are in the system + * configuration list. A "candidate" is up, not loopback and not + * point to point. + */ +void +init_all() +{ + char inbuf[8192]; + struct ifconf ifc; + struct ifreq *ifr; + int fd; + int i, len; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + err(FATAL, "socket: %s", strerror(errno)); + /* NOTREACHED */ + } + + ifc.ifc_len = sizeof(inbuf); + ifc.ifc_buf = inbuf; + if (ioctl(fd, SIOCGIFCONF, (caddr_t)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + err(FATAL, "init_all: SIOCGIFCONF: %s", strerror(errno)); + /* NOTREACHED */ + } + ifr = ifc.ifc_req; + for (i = 0; i < ifc.ifc_len; + i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { + len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) { + err(FATAL, "init_all: SIOCGIFFLAGS: %s", + strerror(errno)); + /* NOTREACHED */ + } + if ((ifr->ifr_flags & + (IFF_UP | IFF_LOOPBACK | IFF_POINTOPOINT)) != IFF_UP) + continue; + init_one(ifr->ifr_name); + } + (void) close(fd); +} + +void +usage() +{ + (void) fprintf(stderr, "usage: rarpd -a [ -d -f ]\n"); + (void) fprintf(stderr, " rarpd [ -d -f ] interface\n"); + exit(1); +} + +static int +bpf_open() +{ + int fd; + int n = 0; + char device[sizeof "/dev/bpf000"]; + + /* Go through all the minors and find one that isn't in use. */ + do { + (void) snprintf(device, sizeof(device), "/dev/bpf%d", n++); + fd = open(device, O_RDWR); + } while (fd < 0 && errno == EBUSY); + + if (fd < 0) { + err(FATAL, "%s: %s", device, strerror(errno)); + /* NOTREACHED */ + } + return fd; +} +/* + * Open a BPF file and attach it to the interface named 'device'. + * Set immediate mode, and set a filter that accepts only RARP requests. + */ +int +rarp_open(device) + char *device; +{ + int fd; + struct ifreq ifr; + u_int dlt; + int immediate; + + static struct bpf_insn insns[] = { + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_REVARP, 0, 3), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ARPOP_REVREQUEST, 0, 1), + BPF_STMT(BPF_RET | BPF_K, sizeof(struct ether_arp) + + sizeof(struct ether_header)), + BPF_STMT(BPF_RET | BPF_K, 0), + }; + static struct bpf_program filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + fd = bpf_open(); + + /* Set immediate mode so packets are processed as they arrive. */ + immediate = 1; + if (ioctl(fd, BIOCIMMEDIATE, &immediate) < 0) { + err(FATAL, "BIOCIMMEDIATE: %s", strerror(errno)); + /* NOTREACHED */ + } + (void) strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name); + if (ioctl(fd, BIOCSETIF, (caddr_t) & ifr) < 0) { + err(FATAL, "BIOCSETIF: %s", strerror(errno)); + /* NOTREACHED */ + } + /* Check that the data link layer is an Ethernet; this code won't work + * with anything else. */ + if (ioctl(fd, BIOCGDLT, (caddr_t) & dlt) < 0) { + err(FATAL, "BIOCGDLT: %s", strerror(errno)); + /* NOTREACHED */ + } + if (dlt != DLT_EN10MB) { + err(FATAL, "%s is not an ethernet", device); + /* NOTREACHED */ + } + /* Set filter program. */ + if (ioctl(fd, BIOCSETF, (caddr_t) & filter) < 0) { + err(FATAL, "BIOCSETF: %s", strerror(errno)); + /* NOTREACHED */ + } + return fd; +} +/* + * Perform various sanity checks on the RARP request packet. Return + * false on failure and log the reason. + */ +static int +rarp_check(p, len) + u_char *p; + int len; +{ + struct ether_header *ep = (struct ether_header *) p; + struct ether_arp *ap = (struct ether_arp *) (p + sizeof(*ep)); + + (void) debug("got a packet"); + + if (len < sizeof(*ep) + sizeof(*ap)) { + err(NONFATAL, "truncated request"); + return 0; + } + /* XXX This test might be better off broken out... */ + if (ntohs (ep->ether_type) != ETHERTYPE_REVARP || + ntohs (ap->arp_hrd) != ARPHRD_ETHER || + ntohs (ap->arp_op) != ARPOP_REVREQUEST || + ntohs (ap->arp_pro) != ETHERTYPE_IP || + ap->arp_hln != 6 || ap->arp_pln != 4) { + err(NONFATAL, "request fails sanity check"); + return 0; + } + if (bcmp((char *) &ep->ether_shost, (char *) &ap->arp_sha, 6) != 0) { + err(NONFATAL, "ether/arp sender address mismatch"); + return 0; + } + if (bcmp((char *) &ap->arp_sha, (char *) &ap->arp_tha, 6) != 0) { + err(NONFATAL, "ether/arp target address mismatch"); + return 0; + } + return 1; +} + +/* + * Loop indefinitely listening for RARP requests on the + * interfaces in 'iflist'. + */ +void +rarp_loop() +{ + u_char *buf, *bp, *ep; + int cc, fd; + fd_set fds, listeners; + int bufsize, maxfd = 0; + struct if_info *ii; + + if (iflist == 0) { + err(FATAL, "no interfaces"); + /* NOTREACHED */ + } + if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t) & bufsize) < 0) { + err(FATAL, "BIOCGBLEN: %s", strerror(errno)); + /* NOTREACHED */ + } + buf = (u_char *) malloc((unsigned) bufsize); + if (buf == 0) { + err(FATAL, "malloc: %s", strerror(errno)); + /* NOTREACHED */ + } + /* + * Find the highest numbered file descriptor for select(). + * Initialize the set of descriptors to listen to. + */ + FD_ZERO(&fds); + for (ii = iflist; ii; ii = ii->ii_next) { + FD_SET(ii->ii_fd, &fds); + if (ii->ii_fd > maxfd) + maxfd = ii->ii_fd; + } + while (1) { + listeners = fds; + if (select(maxfd + 1, &listeners, (struct fd_set *) 0, + (struct fd_set *) 0, (struct timeval *) 0) < 0) { + err(FATAL, "select: %s", strerror(errno)); + /* NOTREACHED */ + } + for (ii = iflist; ii; ii = ii->ii_next) { + fd = ii->ii_fd; + if (!FD_ISSET(fd, &listeners)) + continue; + again: + cc = read(fd, (char *) buf, bufsize); + /* Don't choke when we get ptraced */ + if (cc < 0 && errno == EINTR) + goto again; + /* Due to a SunOS bug, after 2^31 bytes, the file + * offset overflows and read fails with EINVAL. The + * lseek() to 0 will fix things. */ + if (cc < 0) { + if (errno == EINVAL && + (lseek(fd, 0, SEEK_CUR) + bufsize) < 0) { + (void) lseek(fd, 0, 0); + goto again; + } + err(FATAL, "read: %s", strerror(errno)); + /* NOTREACHED */ + } + /* Loop through the packet(s) */ +#define bhp ((struct bpf_hdr *)bp) + bp = buf; + ep = bp + cc; + while (bp < ep) { + register int caplen, hdrlen; + + caplen = bhp->bh_caplen; + hdrlen = bhp->bh_hdrlen; + if (rarp_check(bp + hdrlen, caplen)) + rarp_process(ii, bp + hdrlen); + bp += BPF_WORDALIGN(hdrlen + caplen); + } + } + } +} +#ifndef TFTP_DIR +#define TFTP_DIR "/tftpboot" +#endif + +/* + * True if this server can boot the host whose IP address is 'addr'. + * This check is made by looking in the tftp directory for the + * configuration file. + */ +int +rarp_bootable(addr) + in_addr_t addr; +{ + register struct dirent *dent; + register DIR *d; + char ipname[9]; + static DIR *dd = 0; + + (void) snprintf(ipname, sizeof(ipname), "%08X", addr); + /* If directory is already open, rewind it. Otherwise, open it. */ + if ((d = dd) != NULL) + rewinddir(d); + else { + if (chdir(TFTP_DIR) == -1) { + err(FATAL, "chdir: %s", strerror(errno)); + /* NOTREACHED */ + } + d = opendir("."); + if (d == 0) { + err(FATAL, "opendir: %s", strerror(errno)); + /* NOTREACHED */ + } + dd = d; + } + while ((dent = readdir(d)) != NULL) + if (strncmp(dent->d_name, ipname, 8) == 0) + return 1; + return 0; +} +/* + * Given a list of IP addresses, 'alist', return the first address that + * is on network 'net'; 'netmask' is a mask indicating the network portion + * of the address. + */ +in_addr_t +choose_ipaddr(alist, net, netmask) + in_addr_t **alist; + in_addr_t net; + in_addr_t netmask; +{ + for (; *alist; ++alist) { + if ((**alist & netmask) == net) + return **alist; + } + return 0; +} +/* + * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has + * already been checked for validity. The reply is overlaid on the request. + */ +void +rarp_process(ii, pkt) + struct if_info *ii; + u_char *pkt; +{ + struct ether_header *ep; + struct hostent *hp; + in_addr_t target_ipaddr; + char ename[256]; + struct in_addr in; + + ep = (struct ether_header *) pkt; + + if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0 || + (hp = gethostbyname(ename)) == 0) + return; + + /* Choose correct address from list. */ + if (hp->h_addrtype != AF_INET) { + err(FATAL, "cannot handle non IP addresses"); + /* NOTREACHED */ + } + target_ipaddr = choose_ipaddr((in_addr_t **) hp->h_addr_list, + ii->ii_ipaddr & ii->ii_netmask, ii->ii_netmask); + + if (target_ipaddr == 0) { + in.s_addr = ii->ii_ipaddr & ii->ii_netmask; + err(NONFATAL, "cannot find %s on net %s\n", + ename, inet_ntoa(in)); + return; + } + if (rarp_bootable(htonl(target_ipaddr))) + rarp_reply(ii, ep, target_ipaddr); +} +/* + * Lookup the ethernet address of the interface attached to the BPF + * file descriptor 'fd'; return it in 'eaddr'. + */ +void +lookup_eaddr(ifname, eaddr) + char *ifname; + u_char *eaddr; +{ + char inbuf[8192]; + struct ifconf ifc; + struct ifreq *ifr; + struct sockaddr_dl *sdl; + int fd; + int i, len; + + /* We cannot use SIOCGIFADDR on the BPF descriptor. + We must instead get all the interfaces with SIOCGIFCONF + and find the right one. */ + + /* Use datagram socket to get Ethernet address. */ + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + err(FATAL, "socket: %s", strerror(errno)); + /* NOTREACHED */ + } + + ifc.ifc_len = sizeof(inbuf); + ifc.ifc_buf = inbuf; + if (ioctl(fd, SIOCGIFCONF, (caddr_t)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + err(FATAL, "lookup_eaddr: SIOGIFCONF: %s", strerror(errno)); + /* NOTREACHED */ + } + ifr = ifc.ifc_req; + for (i = 0; i < ifc.ifc_len; + i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { + len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + sdl = (struct sockaddr_dl *)&ifr->ifr_addr; + if (sdl->sdl_family != AF_LINK || sdl->sdl_type != IFT_ETHER || + sdl->sdl_alen != 6) + continue; + if (!strncmp(ifr->ifr_name, ifname, sizeof(ifr->ifr_name))) { + bcopy((caddr_t)LLADDR(sdl), (caddr_t)eaddr, 6); + if (dflag) + fprintf(stderr, "%s: %x:%x:%x:%x:%x:%x\n", + ifr->ifr_name, eaddr[0], eaddr[1], + eaddr[2], eaddr[3], eaddr[4], eaddr[5]); + return; + } + } + + err(FATAL, "lookup_eaddr: Never saw interface `%s'!", ifname); +} +/* + * Lookup the IP address and network mask of the interface named 'ifname'. + */ +void +lookup_ipaddr(ifname, addrp, netmaskp) + char *ifname; + in_addr_t *addrp; + in_addr_t *netmaskp; +{ + int fd; + struct ifreq ifr; + + /* Use datagram socket to get IP address. */ + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + err(FATAL, "socket: %s", strerror(errno)); + /* NOTREACHED */ + } + (void) strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); + if (ioctl(fd, SIOCGIFADDR, (char *) &ifr) < 0) { + err(FATAL, "SIOCGIFADDR: %s", strerror(errno)); + /* NOTREACHED */ + } + *addrp = ((struct sockaddr_in *) & ifr.ifr_addr)->sin_addr.s_addr; + if (ioctl(fd, SIOCGIFNETMASK, (char *) &ifr) < 0) { + perror("SIOCGIFNETMASK"); + exit(1); + } + *netmaskp = ((struct sockaddr_in *) & ifr.ifr_addr)->sin_addr.s_addr; + /* If SIOCGIFNETMASK didn't work, figure out a mask from the IP + * address class. */ + if (*netmaskp == 0) + *netmaskp = ipaddrtonetmask(*addrp); + + (void) close(fd); +} +/* + * Poke the kernel arp tables with the ethernet/ip address combinataion + * given. When processing a reply, we must do this so that the booting + * host (i.e. the guy running rarpd), won't try to ARP for the hardware + * address of the guy being booted (he cannot answer the ARP). + */ +void +update_arptab(ep, ipaddr) + u_char *ep; + in_addr_t ipaddr; +{ + //int s; + struct arpreq request; + struct sockaddr_in *sin; + + request.arp_flags = 0; + sin = (struct sockaddr_in *) & request.arp_pa; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ipaddr; + request.arp_ha.sa_family = AF_UNSPEC; + /* This is needed #if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN, + because AF_UNSPEC is zero and the kernel assumes that a zero + sa_family means that the real sa_family value is in sa_len. */ + request.arp_ha.sa_len = 16; /* XXX */ + bcopy((char *) ep, (char *) request.arp_ha.sa_data, 6); + +#if 0 + s = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(s, SIOCSARP, (caddr_t) & request) < 0) { + err(NONFATAL, "SIOCSARP: %s", strerror(errno)); + } + (void) close(s); +#endif +} +/* + * Build a reverse ARP packet and sent it out on the interface. + * 'ep' points to a valid ARPOP_REVREQUEST. The ARPOP_REVREPLY is built + * on top of the request, then written to the network. + * + * RFC 903 defines the ether_arp fields as follows. The following comments + * are taken (more or less) straight from this document. + * + * ARPOP_REVREQUEST + * + * arp_sha is the hardware address of the sender of the packet. + * arp_spa is undefined. + * arp_tha is the 'target' hardware address. + * In the case where the sender wishes to determine his own + * protocol address, this, like arp_sha, will be the hardware + * address of the sender. + * arp_tpa is undefined. + * + * ARPOP_REVREPLY + * + * arp_sha is the hardware address of the responder (the sender of the + * reply packet). + * arp_spa is the protocol address of the responder (see the note below). + * arp_tha is the hardware address of the target, and should be the same as + * that which was given in the request. + * arp_tpa is the protocol address of the target, that is, the desired address. + * + * Note that the requirement that arp_spa be filled in with the responder's + * protocol is purely for convenience. For instance, if a system were to use + * both ARP and RARP, then the inclusion of the valid protocol-hardware + * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent + * ARP request. + */ +void +rarp_reply(ii, ep, ipaddr) + struct if_info *ii; + struct ether_header *ep; + in_addr_t ipaddr; +{ + int n; + struct ether_arp *ap = (struct ether_arp *) (ep + 1); + int len; + + update_arptab((u_char *) & ap->arp_sha, ipaddr); + + /* Build the rarp reply by modifying the rarp request in place. */ + ep->ether_type = htons(ETHERTYPE_REVARP); + ap->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); + ap->ea_hdr.ar_pro = htons(ETHERTYPE_IP); + ap->arp_op = htons(ARPOP_REVREPLY); + + bcopy((char *) &ap->arp_sha, (char *) &ep->ether_dhost, 6); + bcopy((char *) ii->ii_eaddr, (char *) &ep->ether_shost, 6); + bcopy((char *) ii->ii_eaddr, (char *) &ap->arp_sha, 6); + + bcopy((char *) &ipaddr, (char *) ap->arp_tpa, 4); + /* Target hardware is unchanged. */ + bcopy((char *) &ii->ii_ipaddr, (char *) ap->arp_spa, 4); + + len = sizeof(*ep) + sizeof(*ap); + n = write(ii->ii_fd, (char *) ep, len); + if (n != len) { + err(NONFATAL, "write: only %d of %d bytes written", n, len); + } +} +/* + * Get the netmask of an IP address. This routine is used if + * SIOCGIFNETMASK doesn't work. + */ +in_addr_t +ipaddrtonetmask(addr) + in_addr_t addr; +{ + if (IN_CLASSA(addr)) + return IN_CLASSA_NET; + if (IN_CLASSB(addr)) + return IN_CLASSB_NET; + if (IN_CLASSC(addr)) + return IN_CLASSC_NET; + err(FATAL, "unknown IP address class: %08X", addr); + /* NOTREACHED */ + return 0; +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(int fatal, const char *fmt,...) +#else +err(fmt, va_alist) + int fatal; + char *fmt; +va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (dflag) { + if (fatal) + (void) fprintf(stderr, "rarpd: error: "); + else + (void) fprintf(stderr, "rarpd: warning: "); + (void) vfprintf(stderr, fmt, ap); + (void) fprintf(stderr, "\n"); + } + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + if (fatal) + exit(1); + /* NOTREACHED */ +} + +void +#if __STDC__ +debug(const char *fmt,...) +#else +debug(fmt, va_alist) + char *fmt; +va_dcl +#endif +{ + va_list ap; + + if (dflag) { +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) fprintf(stderr, "rarpd: "); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + (void) fprintf(stderr, "\n"); + } +} diff --git a/network_cmds/route.tproj/gen_header.pl b/network_cmds/route.tproj/gen_header.pl new file mode 100755 index 0000000..dfd9963 --- /dev/null +++ b/network_cmds/route.tproj/gen_header.pl @@ -0,0 +1,15 @@ +#!/usr/local/bin/perl -n +# +# Too run convert the keywords run the following command +# gen_header.pl keywords > keywords.h + +next if m/^#/; +next if m/^$/; +$line_no++; +chop; +$keyword = $_; +$upper = $keyword; +$upper =~ tr/a-z/A-Z/; + +printf "#define\tK_%s\t%d\n\t{\"%s\", K_%s},\n", + $upper, $line_no, $keyword, $upper; diff --git a/network_cmds/route.tproj/keywords b/network_cmds/route.tproj/keywords new file mode 100644 index 0000000..8e740e3 --- /dev/null +++ b/network_cmds/route.tproj/keywords @@ -0,0 +1,49 @@ +# @(#)keywords 8.2 (Berkeley) 3/19/94 + +add +atalk +blackhole +change +cloning +delete +dst +expire +flush +gateway +genmask +get +host +hopcount +iface +ifscope +interface +ifa +ifp +inet +inet6 +iso +link +llinfo +lock +lockrest +mask +monitor +mtu +net +netmask +nostatic +osi +prefixlen +proto1 +proto2 +recvpipe +reject +rtt +rttvar +sa +sendpipe +ssthresh +static +x25 +xns +xresolve diff --git a/network_cmds/route.tproj/keywords.h b/network_cmds/route.tproj/keywords.h new file mode 100644 index 0000000..b9edd54 --- /dev/null +++ b/network_cmds/route.tproj/keywords.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +#define K_ADD 1 + {"add", K_ADD}, +#define K_BLACKHOLE 2 + {"blackhole", K_BLACKHOLE}, +#define K_CHANGE 3 + {"change", K_CHANGE}, +#define K_CLONING 4 + {"cloning", K_CLONING}, +#define K_DELETE 5 + {"delete", K_DELETE}, +#define K_DST 6 + {"dst", K_DST}, +#define K_EXPIRE 7 + {"expire", K_EXPIRE}, +#define K_FLUSH 8 + {"flush", K_FLUSH}, +#define K_GATEWAY 9 + {"gateway", K_GATEWAY}, +#define K_GENMASK 10 + {"genmask", K_GENMASK}, +#define K_GET 11 + {"get", K_GET}, +#define K_HOST 12 + {"host", K_HOST}, +#define K_HOPCOUNT 13 + {"hopcount", K_HOPCOUNT}, +#define K_IFACE 14 + {"iface", K_IFACE}, +#define K_INTERFACE 15 + {"interface", K_INTERFACE}, +#define K_IFA 16 + {"ifa", K_IFA}, +#define K_IFP 17 + {"ifp", K_IFP}, +#define K_INET 18 + {"inet", K_INET}, +#define K_INET6 19 + {"inet6", K_INET6}, +#define K_ISO 20 + {"iso", K_ISO}, +#define K_LINK 21 + {"link", K_LINK}, +#define K_LLINFO 22 + {"llinfo", K_LLINFO}, +#define K_LOCK 23 + {"lock", K_LOCK}, +#define K_LOCKREST 24 + {"lockrest", K_LOCKREST}, +#define K_MASK 25 + {"mask", K_MASK}, +#define K_MONITOR 26 + {"monitor", K_MONITOR}, +#define K_MTU 27 + {"mtu", K_MTU}, +#define K_NET 28 + {"net", K_NET}, +#define K_NETMASK 29 + {"netmask", K_NETMASK}, +#define K_NOSTATIC 30 + {"nostatic", K_NOSTATIC}, +#define K_OSI 31 + {"osi", K_OSI}, +#define K_PREFIXLEN 32 + {"prefixlen", K_PREFIXLEN}, +#define K_PROTO1 33 + {"proto1", K_PROTO1}, +#define K_PROTO2 34 + {"proto2", K_PROTO2}, +#define K_RECVPIPE 35 + {"recvpipe", K_RECVPIPE}, +#define K_REJECT 36 + {"reject", K_REJECT}, +#define K_RTT 37 + {"rtt", K_RTT}, +#define K_RTTVAR 38 + {"rttvar", K_RTTVAR}, +#define K_SA 39 + {"sa", K_SA}, +#define K_SENDPIPE 40 + {"sendpipe", K_SENDPIPE}, +#define K_SSTHRESH 41 + {"ssthresh", K_SSTHRESH}, +#define K_STATIC 42 + {"static", K_STATIC}, +#define K_X25 43 + {"x25", K_X25}, +#define K_XNS 44 + {"xns", K_XNS}, +#define K_XRESOLVE 45 + {"xresolve", K_XRESOLVE}, +#define K_IFSCOPE 46 + {"ifscope", K_IFSCOPE}, diff --git a/network_cmds/route.tproj/route.8 b/network_cmds/route.tproj/route.8 new file mode 100644 index 0000000..74609c9 --- /dev/null +++ b/network_cmds/route.tproj/route.8 @@ -0,0 +1,405 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)route.8 8.3 (Berkeley) 3/19/94 +.\" $FreeBSD: src/sbin/route/route.8,v 1.17.2.7 2001/10/02 10:04:01 ru Exp $ +.\" +.Dd June 8, 2001 +.Dt ROUTE 8 +.Os BSD 4.4 +.Sh NAME +.Nm route +.Nd manually manipulate the routing tables +.Sh SYNOPSIS +.Nm +.Op Fl dnqtv +.Ar command +.Oo +.Op Ar modifiers +.Ar args +.Oc +.Sh DESCRIPTION +.Nm Route +is a utility used to manually manipulate the network +routing tables. It normally is not needed, as a +system routing table management daemon such as +.Xr routed 8 , +should tend to this task. +.Pp +The +.Nm +utility supports a limited number of general options, +but a rich command language, enabling the user to specify +any arbitrary request that could be delivered via the +programmatic interface discussed in +.Xr route 4 . +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl d +Run in debug-only mode, i.e., do not actually modify the routing table. +.It Fl n +Bypass attempts to print host and network names symbolically +when reporting actions. (The process of translating between symbolic +names and numerical equivalents can be quite time consuming, and +may require correct operation of the network; thus it may be expedient +to forget this, especially when attempting to repair networking operations). +.It Fl t +Run in test-only mode. +.Pa /dev/null +is used instead of a socket. +.It Fl v +(verbose) Print additional details. +.It Fl q +Suppress all output. +.El +.Pp +The +.Nm +utility provides six commands: +.Pp +.Bl -tag -width Fl -compact +.It Cm add +Add a route. +.It Cm flush +Remove all routes. +.It Cm delete +Delete a specific route. +.It Cm change +Change aspects of a route (such as its gateway). +.It Cm get +Lookup and display the route for a destination. +.It Cm monitor +Continuously report any changes to the routing information base, +routing lookup misses, or suspected network partitionings. +.El +.Pp +The monitor command has the syntax: +.Pp +.Bd -ragged -offset indent -compact +.Nm +.Op Fl n +.Cm monitor +.Ed +.Pp +The flush command has the syntax: +.Pp +.Bd -ragged -offset indent -compact +.Nm +.Op Fl n +.Cm flush +.Op Ar family +.Ed +.Pp +If the +.Cm flush +command is specified, +.Nm +will ``flush'' the routing tables of all gateway entries. +When the address family may is specified by any of the +.Fl osi , +.Fl xns , +.Fl atalk , +.Fl inet6 , +or +.Fl inet +modifiers, only routes having destinations with addresses in the +delineated family will be deleted. +.Pp +The other commands have the following syntax: +.Pp +.Bd -ragged -offset indent -compact +.Nm +.Op Fl n +.Ar command +.Op Fl net No \&| Fl host +.Oo Fl ifscope +.Ar boundif +.Oc +.Ar destination gateway +.Op Ar netmask +.Ed +.Pp +where +.Ar destination +is the destination host or network, +.Ar gateway +is the next-hop intermediary via which packets should be routed. +Routes to a particular host may be distinguished from those to +a network by interpreting the Internet address specified as the +.Ar destination +argument. +The optional modifiers +.Fl net +and +.Fl host +force the destination to be interpreted as a network or a host, respectively. +Otherwise, if the +.Ar destination +has a +.Dq local address part +of +INADDR_ANY +.Pq Li 0.0.0.0 , +or if the +.Ar destination +is the symbolic name of a network, then the route is +assumed to be to a network; otherwise, it is presumed to be a +route to a host. +Optionally, the +.Ar destination +could also be specified in the +.Ar net Ns / Ns Ar bits +format. +.Pp +For example, +.Li 128.32 +is interpreted as +.Fl host Li 128.0.0.32 ; +.Li 128.32.130 +is interpreted as +.Fl host Li 128.32.0.130 ; +.Fl net Li 128.32 +is interpreted as +.Li 128.32.0.0; +.Fl net Li 128.32.130 +is interpreted as +.Li 128.32.130.0; +and +.Li 192.168.64/20 +is interpreted as +.Fl net Li 192.168.64 Fl netmask Li 255.255.240.0 . +.Pp +A +.Ar destination +of +.Ar default +is a synonym for +.Fl net Li 0.0.0.0 , +which is the default route. +.Pp +If the destination is directly reachable +via an interface requiring +no intermediary system to act as a gateway, the +.Fl interface +modifier should be specified; +the gateway given is the address of this host on the common network, +indicating the interface to be used for transmission. +Alternately, if the interface is point to point the name of the interface +itself may be given, in which case the route remains valid even +if the local or remote addresses change. +.Pp +For AF_INET and AF_INET6, the +.Fl ifscope +modifier specifies the additional property of the route related to +the interface scope derived from interface +.Ar boundif . +Such property allows for the presence of multiple route entries with +the same destination, where each route is associated with a unique +interface. This modifier is required in order to manipulate route +entries marked with the RTF_IFSCOPE flag. +.Pp +The optional modifier +.Fl link +specify that all subsequent addresses +are specified as link-level addresses, +and the names must be numeric specifications rather than +symbolic names. +.Pp +The optional +.Fl netmask +modifier is intended +to achieve the effect of an +.Tn OSI +.Tn ESIS +redirect with the netmask option, +or to manually add subnet routes with +netmasks different from that of the implied network interface +(as would otherwise be communicated using the OSPF or ISIS routing protocols). +One specifies an additional ensuing address parameter +(to be interpreted as a network mask). +The implicit network mask generated in the AF_INET case +can be overridden by making sure this option follows the destination parameter. +.Pp +For +.Dv AF_INET6 , +the +.Fl prefixlen +qualifier +is available instead of the +.Fl mask +qualifier because non-continuous masks are not allowed in IPv6. +For example, +.Fl prefixlen Li 32 +specifies network mask of +.Li ffff:ffff:0000:0000:0000:0000:0000:0000 +to be used. +The default value of prefixlen is 64 to get along with +the aggregatable address. +But 0 is assumed if +.Cm default +is specified. +Note that the qualifier works only for +.Dv AF_INET6 +address family. +.Pp +Routes have associated flags which influence operation of the protocols +when sending to destinations matched by the routes. +These flags may be set (or sometimes cleared) +by indicating the following corresponding modifiers: +.Bd -literal +-cloning RTF_CLONING - generates a new route on use +-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup) +-iface ~RTF_GATEWAY - destination is directly reachable +-static RTF_STATIC - manually added route +-nostatic ~RTF_STATIC - pretend route added by kernel or daemon +-reject RTF_REJECT - emit an ICMP unreachable when matched +-blackhole RTF_BLACKHOLE - silently discard pkts (during updates) +-proto1 RTF_PROTO1 - set protocol specific routing flag #1 +-proto2 RTF_PROTO2 - set protocol specific routing flag #2 +-llinfo RTF_LLINFO - validly translates proto addr to link addr +.Ed +.Pp +The optional modifiers +.Fl rtt , +.Fl rttvar , +.Fl sendpipe , +.Fl recvpipe , +.Fl mtu , +.Fl hopcount , +.Fl expire , +and +.Fl ssthresh +provide initial values to quantities maintained in the routing entry +by transport level protocols, such as TCP or TP4. +These may be individually locked by preceding each such modifier to +be locked by +the +.Fl lock +meta-modifier, or one can +specify that all ensuing metrics may be locked by the +.Fl lockrest +meta-modifier. +.Pp +In a +.Cm change +or +.Cm add +command where the destination and gateway are not sufficient to specify +the route (as in the +.Tn ISO +case where several interfaces may have the +same address), the +.Fl ifp +or +.Fl ifa +modifiers may be used to determine the interface or interface address. +.Pp +The optional +.Fl proxy +modifier specifies that the +.Dv RTF_LLINFO +routing table entry is the +.Dq published (proxy-only) +.Tn ARP +entry, as reported by +.Xr arp 8 . +.Pp +All symbolic names specified for a +.Ar destination +or +.Ar gateway +are looked up first as a host name using +.Xr gethostbyname 3 . +If this lookup fails, +.Xr getnetbyname 3 +is then used to interpret the name as that of a network. +.Pp +.Nm Route +uses a routing socket and the new message types +.Dv RTM_ADD , RTM_DELETE , RTM_GET , +and +.Dv RTM_CHANGE . +As such, only the super-user may modify +the routing tables. +.Sh DIAGNOSTICS +.Bl -diag +.It "add [host \&| network ] %s: gateway %s flags %x" +The specified route is being added to the tables. The +values printed are from the routing table entry supplied +in the +.Xr ioctl 2 +call. +If the gateway address used was not the primary address of the gateway +(the first one returned by +.Xr gethostbyname 3 ) , +the gateway address is printed numerically as well as symbolically. +.It "delete [ host \&| network ] %s: gateway %s flags %x" +As above, but when deleting an entry. +.It "%s %s done" +When the +.Cm flush +command is specified, each routing table entry deleted +is indicated with a message of this form. +.It "Network is unreachable" +An attempt to add a route failed because the gateway listed was not +on a directly-connected network. +The next-hop gateway must be given. +.It "not in table" +A delete operation was attempted for an entry which +wasn't present in the tables. +.It "routing table overflow" +An add operation was attempted, but the system was +low on resources and was unable to allocate memory +to create the new entry. +.It "gateway uses the same route" +A +.Cm change +operation resulted in a route whose gateway uses the +same route as the one being changed. +The next-hop gateway should be reachable through a different route. +.El +.Pp +.Ex -std +.Sh SEE ALSO +.Xr netintro 4 , +.Xr route 4 , +.Xr arp 8 , +.Xr routed 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Sh BUGS +The first paragraph may have slightly exaggerated +.Xr routed 8 Ns 's +abilities. diff --git a/network_cmds/route.tproj/route.c b/network_cmds/route.tproj/route.c new file mode 100644 index 0000000..9f7f4bd --- /dev/null +++ b/network_cmds/route.tproj/route.c @@ -0,0 +1,1571 @@ +/* + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static const char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <ifaddrs.h> + +struct keytab { + char *kt_cp; + int kt_i; +} keywords[] = { +#include "keywords.h" + {0, 0} +}; + +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_dl sdl; + struct sockaddr_storage ss; /* added to avoid memory overrun */ +} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp; + +typedef union sockunion *sup; +int pid, rtm_addrs, uid; +int s; +int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword(); +int iflag, verbose, aflen = sizeof (struct sockaddr_in); +int locking, lockrest, debugonly; +struct rt_metrics rt_metrics; +u_long rtm_inits; +unsigned int ifscope; + +static const char *route_strerror(int); +const char *routename(), *netname(); +void flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf(); +void print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr(); +int getaddr(), rtmsg(), x25_makemask(); +int prefixlen(); +extern char *iso_ntoa(); + +static void +inet_makenetandmask(in_addr_t net, struct sockaddr_in *sin, + struct sockaddr_in *sin_mask, in_addr_t bits); + +void usage __P((const char *)) __dead2; + +void +usage(cp) + const char *cp; +{ + if (cp) + warnx("bad keyword: %s", cp); + (void) fprintf(stderr, + "usage: route [-dnqtv] command [[modifiers] args]\n"); + exit(EX_USAGE); + /* NOTREACHED */ +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch; + + if (argc < 2) + usage((char *)NULL); + + while ((ch = getopt(argc, argv, "nqdtv")) != -1) + switch(ch) { + case 'n': + nflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + tflag = 1; + break; + case 'd': + debugonly = 1; + break; + case '?': + default: + usage((char *)NULL); + } + argc -= optind; + argv += optind; + + pid = getpid(); + uid = geteuid(); + if (tflag) + s = open(_PATH_DEVNULL, O_WRONLY, 0); + else + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + err(EX_OSERR, "socket"); + setuid(uid); + if (*argv) + switch (keyword(*argv)) { + case K_GET: + uid = 0; + /* FALLTHROUGH */ + + case K_CHANGE: + case K_ADD: + case K_DELETE: + newroute(argc, argv); + exit(0); + /* NOTREACHED */ + + case K_MONITOR: + monitor(); + /* NOTREACHED */ + + case K_FLUSH: + flushroutes(argc, argv); + exit(0); + /* NOTREACHED */ + } + usage(*argv); + /* NOTREACHED */ +} + +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +void +flushroutes(argc, argv) + int argc; + char *argv[]; +{ + size_t needed; + int mib[6], rlen, seqno; + char *buf, *next, *lim; + register struct rt_msghdr *rtm; + + if (uid) { + errx(EX_NOPERM, "must be root to alter routing table"); + } + shutdown(s, 0); /* Don't want to read back our messages */ + if (argc > 1) { + argv++; + if (argc == 2 && **argv == '-') + switch (keyword(*argv + 1)) { + case K_INET: + af = AF_INET; + break; +#ifdef INET6 + case K_INET6: + af = AF_INET6; + break; +#endif + case K_LINK: + af = AF_LINK; + break; + default: + goto bad; + } else +bad: usage(*argv); + } + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(EX_OSERR, "route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + errx(EX_OSERR, "malloc failed"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + err(EX_OSERR, "route-sysctl-get"); + lim = buf + needed; + if (verbose) + (void) printf("Examining routing table from sysctl\n"); + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & RTF_GATEWAY) == 0) + continue; + if (af) { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + if (sa->sa_family != af) + continue; + } + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + rlen = write(s, next, rtm->rtm_msglen); + if (rlen < (int)rtm->rtm_msglen) { + warn("write to routing socket"); + (void) printf("got only %d for rlen\n", rlen); + break; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? + routename(sa) : netname(sa)); + sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa); + (void) printf("%-20.20s ", routename(sa)); + (void) printf("done\n"); + } + } +} + +const char * +routename(sa) + struct sockaddr *sa; +{ + register char *cp; + static char line[MAXHOSTNAMELEN + 1]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + + if (first) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = index(domain, '.'))) { + domain[MAXHOSTNAMELEN] = '\0'; + (void) memmove(domain, cp + 1, strlen(cp + 1) + 1); + } else + domain[0] = 0; + } + + if (sa->sa_len == 0) + strlcpy(line, "default", sizeof(line)); + else switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + cp = 0; + if (in.s_addr == INADDR_ANY || sa->sa_len < 4) + cp = "default"; + if (cp == 0 && !nflag) { + hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), + AF_INET); + if (hp) { + if ((cp = index(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + if (cp) { + strlcpy(line, cp, sizeof(line)); + } else { + /* XXX - why not inet_ntoa()? */ +#define C(x) (unsigned)((x) & 0xff) + in.s_addr = ntohl(in.s_addr); + (void) snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); + } + break; + } + +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; /* use static var for safety */ + int niflags = 0; +#ifdef NI_WITHSCOPEID + niflags = NI_WITHSCOPEID; +#endif + + memset(&sin6, 0, sizeof(sin6)); + memcpy(&sin6, sa, sa->sa_len); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; +#ifdef __KAME__ + if (sa->sa_len == sizeof(struct sockaddr_in6) && + (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && + sin6.sin6_scope_id == 0) { + sin6.sin6_scope_id = + ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } +#endif + if (nflag) + niflags |= NI_NUMERICHOST; + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + line, sizeof(line), NULL, 0, niflags) != 0) + strlcpy(line, "invalid", sizeof(line)); + + return(line); + } +#endif + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + default: + { u_short *s = (u_short *)sa; + u_short *slim = s + ((sa->sa_len + 1) >> 1); + char *cp = line + snprintf(line, sizeof(line), "(%d)", sa->sa_family); + char *cpe = line + sizeof(line); + + while (++s < slim && cp < cpe) /* start with sa->sa_data */ + cp += snprintf(cp, cpe - cp, " %x", *s); + break; + } + } + return (line); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net, not a host. + */ +const char * +netname(sa) + struct sockaddr *sa; +{ + char *cp = NULL; + static char line[MAXHOSTNAMELEN + 1]; + struct netent *np = NULL; + register in_addr_t i; + + switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + i = in.s_addr = ntohl(in.s_addr); + if (in.s_addr == 0) + cp = "default"; + else if (!nflag) { + np = getnetbyaddr(i, AF_INET); + if (np != NULL) + cp = np->n_name; + } +#define C(x) (unsigned)((x) & 0xff) + if (cp != NULL) + strlcpy(line, cp, sizeof(line)); + else if ((in.s_addr & 0xffffff) == 0) + (void) sprintf(line, "%u", C(in.s_addr >> 24)); + else if ((in.s_addr & 0xffff) == 0) + (void) sprintf(line, "%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16)); + else if ((in.s_addr & 0xff) == 0) + (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8)); + else + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), + C(in.s_addr)); +#undef C + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; /* use static var for safety */ + int niflags = 0; +#ifdef NI_WITHSCOPEID + niflags = NI_WITHSCOPEID; +#endif + + memset(&sin6, 0, sizeof(sin6)); + memcpy(&sin6, sa, sa->sa_len); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; +#ifdef __KAME__ + if (sa->sa_len == sizeof(struct sockaddr_in6) && + (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && + sin6.sin6_scope_id == 0) { + sin6.sin6_scope_id = + ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } +#endif + if (nflag) + niflags |= NI_NUMERICHOST; + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + line, sizeof(line), NULL, 0, niflags) != 0) + strlcpy(line, "invalid", sizeof(line)); + + return(line); + } +#endif + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + + default: + { u_short *s = (u_short *)sa->sa_data; + u_short *slim = s + ((sa->sa_len + 1)>>1); + char *cp = line + snprintf(line, sizeof(line), "af %d:", sa->sa_family); + char *cpe = line + sizeof(line); + + while (s < slim && cp < cpe) + cp += snprintf(cp, cpe - cp, " %x", *s++); + break; + } + } + return (line); +} + +static const char * +route_strerror(int error) +{ + + switch (error) { + case ESRCH: + return "not in table"; + case EBUSY: + return "entry in use"; + case ENOBUFS: + return "routing table overflow"; + default: + return (strerror(error)); + } +} + +void +set_metric(value, key) + char *value; + int key; +{ + int flag = 0; + u_int noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) case x: valp = (u_int *)&rt_metrics.z; flag = y; break + caseof(K_MTU, RTV_MTU, rmx_mtu); + caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); + caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); + caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); + caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); + caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); + caseof(K_RTT, RTV_RTT, rmx_rtt); + caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + *valp = atoi(value); +} + +void +newroute(argc, argv) + int argc; + register char **argv; +{ + char *cmd, *dest = "", *gateway = ""; + int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC; + int key; + struct hostent *hp = 0; + + if (uid) { + errx(EX_NOPERM, "must be root to alter routing table"); + } + cmd = argv[0]; + if (*cmd != 'g') + shutdown(s, 0); /* Don't want to read back our messages */ + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; +#ifdef INET6 + case K_INET6: + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); + break; +#endif + case K_SA: + af = PF_ROUTE; + aflen = sizeof(union sockunion); + break; + case K_IFACE: + case K_INTERFACE: + iflag++; + break; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LLINFO: + flags |= RTF_LLINFO; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + forcehost++; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_CLONING: + flags |= RTF_CLONING; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_IFA: + if (!--argc) + usage((char *)NULL); + (void) getaddr(RTA_IFA, *++argv, 0); + break; + case K_IFP: + if (!--argc) + usage((char *)NULL); + (void) getaddr(RTA_IFP, *++argv, 0); + break; + case K_GENMASK: + if (!--argc) + usage((char *)NULL); + (void) getaddr(RTA_GENMASK, *++argv, 0); + break; + case K_GATEWAY: + if (!--argc) + usage((char *)NULL); + (void) getaddr(RTA_GATEWAY, *++argv, 0); + break; + case K_DST: + if (!--argc) + usage((char *)NULL); + ishost = getaddr(RTA_DST, *++argv, &hp); + dest = *argv; + break; + case K_NETMASK: + if (!--argc) + usage((char *)NULL); + (void) getaddr(RTA_NETMASK, *++argv, 0); + /* FALLTHROUGH */ + case K_NET: + forcenet++; + break; + case K_PREFIXLEN: + if (!--argc) + usage((char *)NULL); + if (prefixlen(*++argv) == -1) { + forcenet = 0; + ishost = 1; + } else { + forcenet = 1; + ishost = 0; + } + break; + case K_MTU: + case K_HOPCOUNT: + case K_EXPIRE: + case K_RECVPIPE: + case K_SENDPIPE: + case K_SSTHRESH: + case K_RTT: + case K_RTTVAR: + if (!--argc) + usage((char *)NULL); + set_metric(*++argv, key); + break; + case K_IFSCOPE: + if (!--argc) + usage((char *)NULL); + if ((ifscope = if_nametoindex(*++argv)) != 0) + flags |= RTF_IFSCOPE; + else + errx(1, "bad interface name"); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + ishost = getaddr(RTA_DST, *argv, &hp); + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + (void) getaddr(RTA_GATEWAY, *argv, &hp); + } else { + (void) getaddr(RTA_NETMASK, *argv, 0); + } + } + } + if (forcehost) { + ishost = 1; +#ifdef INET6 + if (af == AF_INET6) { + rtm_addrs &= ~RTA_NETMASK; + memset((void *)&so_mask, 0, sizeof(so_mask)); + } +#endif + } + if (forcenet) + ishost = 0; + flags |= RTF_UP; + if (ishost) + flags |= RTF_HOST; + if (iflag == 0) + flags |= RTF_GATEWAY; + if (so_mask.sin.sin_family == AF_INET) { + // make sure the mask is contiguous + long i; + for (i = 0; i < 32; i++) + if (((so_mask.sin.sin_addr.s_addr) & ntohl((1 << i))) != 0) + break; + for (; i < 32; i++) + if (((so_mask.sin.sin_addr.s_addr) & ntohl((1 << i))) == 0) + errx(EX_NOHOST, "invalid mask: %s", inet_ntoa(so_mask.sin.sin_addr)); + } + for (attempts = 1; ; attempts++) { + errno = 0; + if ((ret = rtmsg(*cmd, flags)) == 0) + break; + if (errno != ENETUNREACH && errno != ESRCH) + break; + if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { + hp->h_addr_list++; + bcopy(hp->h_addr_list[0], &so_gate.sin.sin_addr, + MIN(hp->h_length, sizeof(so_gate.sin.sin_addr))); + } else + break; + } + if (*cmd == 'g') + exit(0); + oerrno = errno; + (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest); + if (*gateway) { + (void) printf(": gateway %s", gateway); + if (attempts > 1 && ret == 0 && af == AF_INET) + (void) printf(" (%s)", inet_ntoa(so_gate.sin.sin_addr)); + } + if (ret == 0) + (void) printf("\n"); + else { + (void)printf(": %s\n", route_strerror(oerrno)); + } +} + +static void +inet_makenetandmask(in_addr_t net, struct sockaddr_in *sin, + struct sockaddr_in *sin_mask, in_addr_t bits) +{ + in_addr_t mask = 0; + + rtm_addrs |= RTA_NETMASK; + /* + * MSB of net should be meaningful. 0/0 is exception. + */ + if (net > 0) + while ((net & 0xff000000) == 0) + net <<= 8; + + /* + * If no /xx was specified we must calculate the + * CIDR address. + */ + if ((bits == 0) && (net != 0)) { + u_long i, j; + + for(i = 0, j = 0xff; i < 4; i++) { + if (net & j) { + break; + } + j <<= 8; + } + /* i holds the first non zero bit */ + bits = 32 - (i*8); + } + if (bits != 0) + mask = 0xffffffff << (32 - bits); + + sin->sin_addr.s_addr = htonl(net); + sin_mask->sin_addr.s_addr = htonl(mask); + sin_mask->sin_len = sizeof(struct sockaddr_in); + sin_mask->sin_family = AF_INET; +} + +#ifdef INET6 +/* + * XXX the function may need more improvement... + */ +static int +inet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen) +{ + struct in6_addr in6; + + if (plen == NULL) { + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && + sin6->sin6_scope_id == 0) { + plen = "0"; + } else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) { + /* aggregatable global unicast - RFC2374 */ + memset(&in6, 0, sizeof(in6)); + if (!memcmp(&sin6->sin6_addr.s6_addr[8], + &in6.s6_addr[8], 8)) + plen = "64"; + } + } + + if (plen == NULL || strcmp(plen, "128") == 0) + return (1); + rtm_addrs |= RTA_NETMASK; + prefixlen(plen); + return (0); +} +#endif + +/* + * Interpret an argument as a network address of some kind, + * returning 1 if a host address, 0 if a network address. + */ +int +getaddr(which, s, hpp) + int which; + char *s; + struct hostent **hpp; +{ + register sup su = NULL; + struct hostent *hp; + struct netent *np; + in_addr_t val; + char *q; + int afamily; /* local copy of af so we can change it */ + + if (af == 0) { + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + } + afamily = af; + rtm_addrs |= which; + switch (which) { + case RTA_DST: + su = &so_dst; + break; + case RTA_GATEWAY: + su = &so_gate; + if (iflag) { + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl *sdl = NULL; + + if (getifaddrs(&ifap)) + err(1, "getifaddrs"); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + if (strcmp(s, ifa->ifa_name)) + continue; + + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + } + /* If we found it, then use it */ + if (sdl) { + /* + * Copy is safe since we have a + * sockaddr_storage member in sockunion{}. + * Note that we need to copy before calling + * freeifaddrs(). + */ + memcpy(&su->sdl, sdl, sdl->sdl_len); + } + freeifaddrs(ifap); + if (sdl) + return(1); + } + break; + case RTA_NETMASK: + su = &so_mask; + break; + case RTA_GENMASK: + su = &so_genmask; + break; + case RTA_IFP: + su = &so_ifp; + afamily = AF_LINK; + break; + case RTA_IFA: + su = &so_ifa; + break; + default: + usage("internal error"); + /*NOTREACHED*/ + } + su->sa.sa_len = aflen; + su->sa.sa_family = afamily; /* cases that don't want it have left already */ + if (strcmp(s, "default") == 0) { + /* + * Default is net 0.0.0.0/0 + */ + switch (which) { + case RTA_DST: + forcenet++; + /* bzero(su, sizeof(*su)); *//* for readability */ + (void) getaddr(RTA_NETMASK, s, 0); + break; + case RTA_NETMASK: + case RTA_GENMASK: + /* bzero(su, sizeof(*su)); *//* for readability */ + su->sa.sa_len = 0; + break; + } + return (0); + } + switch (afamily) { +#ifdef INET6 + case AF_INET6: + { + struct addrinfo hints, *res; + int ecode; + + q = NULL; + if (which == RTA_DST && (q = strchr(s, '/')) != NULL) + *q = '\0'; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = afamily; /*AF_INET6*/ + hints.ai_flags = AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + ecode = getaddrinfo(s, NULL, &hints, &res); + if (ecode != 0 || res->ai_family != AF_INET6 || + res->ai_addrlen != sizeof(su->sin6)) { + (void) fprintf(stderr, "%s: %s\n", s, + gai_strerror(ecode)); + exit(1); + } + memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6)); +#ifdef __KAME__ + if ((IN6_IS_ADDR_LINKLOCAL(&su->sin6.sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&su->sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&su->sin6.sin6_addr)) && + su->sin6.sin6_scope_id) { + *(u_int16_t *)&su->sin6.sin6_addr.s6_addr[2] = + htons(su->sin6.sin6_scope_id); + su->sin6.sin6_scope_id = 0; + } +#endif + freeaddrinfo(res); + if (hints.ai_flags == AI_NUMERICHOST) { + if (q != NULL) + *q++ = '/'; + if (which == RTA_DST) + return (inet6_makenetandmask(&su->sin6, q)); + return (0); + } else { + return (1); + } + } +#endif /* INET6 */ + + case AF_LINK: + link_addr(s, &su->sdl); + return (1); + + + case PF_ROUTE: + su->sa.sa_len = sizeof(*su); + sockaddr(s, &su->sa); + return (1); + + case AF_INET: + default: + break; + } + + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + + q = strchr(s,'/'); + if (q && which == RTA_DST) { + *q = '\0'; + if ((val = inet_network(s)) != INADDR_NONE) { + inet_makenetandmask( + val, &su->sin, (struct sockaddr_in *)&so_mask, + strtoul(q+1, 0, 0)); + return (0); + } + *q = '/'; + } + if ((which != RTA_DST || forcenet == 0) && + inet_aton(s, &su->sin.sin_addr)) { + val = su->sin.sin_addr.s_addr; + if (which != RTA_DST || forcehost || + inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + return (1); + else { + val = ntohl(val); + goto netdone; + } + } + if (which == RTA_DST && forcehost == 0 && + ((val = inet_network(s)) != INADDR_NONE || + ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0))) { +netdone: + inet_makenetandmask(val, &su->sin, (struct sockaddr_in *)&so_mask, 0); + return (0); + } + hp = gethostbyname(s); + if (hp) { + *hpp = hp; + su->sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (char *)&su->sin.sin_addr, + MIN(hp->h_length, sizeof(su->sin.sin_addr))); + return (1); + } + errx(EX_NOHOST, "bad address: %s", s); +} + +int +prefixlen(s) + char *s; +{ + int len = atoi(s), q, r; + int max; + char *p; + + rtm_addrs |= RTA_NETMASK; + switch (af) { +#ifdef INET6 + case AF_INET6: + max = 128; + p = (char *)&so_mask.sin6.sin6_addr; + break; +#endif + case AF_INET: + max = 32; + p = (char *)&so_mask.sin.sin_addr; + break; + default: + (void) fprintf(stderr, "prefixlen not supported in this af\n"); + exit(1); + /*NOTREACHED*/ + } + + if (len < 0 || max < len) { + (void) fprintf(stderr, "%s: bad value\n", s); + exit(1); + } + + q = len >> 3; + r = len & 7; + so_mask.sa.sa_family = af; + so_mask.sa.sa_len = aflen; + memset((void *)p, 0, max / 8); + if (q > 0) + memset((void *)p, 0xff, q); + if (r > 0) + *((u_char *)p + q) = (0xff00 >> r) & 0xff; + if (len == max) + return -1; + else + return len; +} + +void +interfaces() +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next; + register struct rt_msghdr *rtm; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(EX_OSERR, "route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + errx(EX_OSERR, "malloc failed"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + err(EX_OSERR, "actual retrieval of interface table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } +} + +void +monitor() +{ + int n; + char msg[2048]; + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for(;;) { + time_t now; + n = read(s, msg, 2048); + now = time(NULL); + (void) printf("\ngot message of size %d on %s", n, ctime(&now)); + print_rtmsg((struct rt_msghdr *)msg, n); + } +} + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int +rtmsg(cmd, flags) + int cmd, flags; +{ + static int seq; + int rlen; + register char *cp = m_rtmsg.m_space; + register int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\ + if (verbose) sodump(&(u),"u");\ + } + + errno = 0; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + if (cmd == 'a') + cmd = RTM_ADD; + else if (cmd == 'c') + cmd = RTM_CHANGE; + else if (cmd == 'g') { + cmd = RTM_GET; + if (so_ifp.sa.sa_family == 0) { + so_ifp.sa.sa_family = AF_LINK; + so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } + } else + cmd = RTM_DELETE; +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = cmd; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + rtm.rtm_index = ifscope; + + if (rtm_addrs & RTA_NETMASK) + mask_addr(); + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_GATEWAY, so_gate); + NEXTADDR(RTA_NETMASK, so_mask); + NEXTADDR(RTA_GENMASK, so_genmask); + NEXTADDR(RTA_IFP, so_ifp); + NEXTADDR(RTA_IFA, so_ifa); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose) + print_rtmsg(&rtm, l); + if (debugonly) + return (0); + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + warnx("writing to routing socket: %s", route_strerror(errno)); + return (-1); + } + if (cmd == RTM_GET) { + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + if (l < 0) + warn("read from routing socket"); + else + print_getmsg(&rtm, l); + } +#undef rtm + return (0); +} + +void +mask_addr() +{ + int olen = so_mask.sa.sa_len; + register char *cp1 = olen + (char *)&so_mask, *cp2; + + for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; ) + if (*--cp1 != 0) { + so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask; + break; + } + if ((rtm_addrs & RTA_DST) == 0) + return; + switch (so_dst.sa.sa_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + case AF_APPLETALK: + case 0: + return; + } + cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst; + cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst; + while (cp2 > cp1) + *--cp2 = 0; + cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask; + while (cp1 > so_dst.sa.sa_data) + *--cp1 &= *--cp2; +} + +char *msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + "RTM_NEWMADDR: new multicast group membership on iface", + "RTM_DELMADDR: multicast group membership removed from iface", + 0, +}; + +char metricnames[] = +"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" +"\1mtu"; +char routeflags[] = +"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010DELCLONE" +"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016" +"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024b024" +"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\031IFSCOPE\032CONDEMNED" +"\033IFREF\034PROXY\035ROUTER"; +char ifnetflags[] = +"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" +"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" +"\017LINK2\020MULTICAST"; +char addrnames[] = +"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +void +print_rtmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; +#ifdef RTM_NEWMADDR + struct ifma_msghdr *ifmam; +#endif + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void) printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + (void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen); + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + (void) printf("if# %d, flags:", ifm->ifm_index); + bprintf(stdout, ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + (void) printf("metric %d, flags:", ifam->ifam_metric); + bprintf(stdout, ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); + break; +#ifdef RTM_NEWMADDR + case RTM_NEWMADDR: + case RTM_DELMADDR: + ifmam = (struct ifma_msghdr *)rtm; + pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs); + break; +#endif + default: + (void) printf("pid: %ld, seq %d, errno %d, ", + (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + if (rtm->rtm_flags & RTF_IFSCOPE) + (void) printf("ifscope %d, ", rtm->rtm_index); + if (rtm->rtm_flags & RTF_IFREF) + (void) printf("ifref, "); + (void) printf("flags:"); + bprintf(stdout, rtm->rtm_flags, routeflags); + pmsg_common(rtm); + } +} + +void +print_getmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; + struct sockaddr_dl *ifp = NULL; + register struct sockaddr *sa; + register char *cp; + register int i; + + (void) printf(" route to: %s\n", routename(&so_dst.sa)); + if (rtm->rtm_version != RTM_VERSION) { + warnx("routing message version %d not understood", + rtm->rtm_version); + return; + } + if (rtm->rtm_msglen > msglen) { + warnx("message length mismatch, in packet %d, returned %d", + rtm->rtm_msglen, msglen); + } + if (rtm->rtm_errno) { + errno = rtm->rtm_errno; + warn("message indicates error %d", errno); + return; + } + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + } + ADVANCE(cp, sa); + } + if (dst && mask) + mask->sa_family = dst->sa_family; /* XXX */ + if (dst) + (void)printf("destination: %s\n", routename(dst)); + if (mask) { + int savenflag = nflag; + + nflag = 1; + (void)printf(" mask: %s\n", routename(mask)); + nflag = savenflag; + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) + (void)printf(" gateway: %s\n", routename(gate)); + if (ifp) + (void)printf(" interface: %.*s\n", + ifp->sdl_nlen, ifp->sdl_data); + (void)printf(" flags: "); + bprintf(stdout, rtm->rtm_flags, routeflags); + +#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') +#define msec(u) (((u) + 500) / 1000) /* usec to msec */ + + (void) printf("\n%s\n", "\ + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire"); + printf("%8u%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8u%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8u%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8u%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8u%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); + printf("%8u%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); + printf("%8u%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); + if (rtm->rtm_rmx.rmx_expire) + rtm->rtm_rmx.rmx_expire -= time(0); + printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + if (verbose) + pmsg_common(rtm); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void) printf("sockaddrs: "); + bprintf(stdout, rtm->rtm_addrs, addrnames); + putchar('\n'); + } +#undef RTA_IGN +} + +void +pmsg_common(rtm) + register struct rt_msghdr *rtm; +{ + (void) printf("\nlocks: "); + bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); + (void) printf(" inits: "); + bprintf(stdout, rtm->rtm_inits, metricnames); + pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs); +} + +void +pmsg_addrs(cp, addrs) + char *cp; + int addrs; +{ + register struct sockaddr *sa; + int i; + + if (addrs == 0) { + (void) putchar('\n'); + return; + } + (void) printf("\nsockaddrs: "); + bprintf(stdout, addrs, addrnames); + (void) putchar('\n'); + for (i = 1; i; i <<= 1) + if (i & addrs) { + sa = (struct sockaddr *)cp; + (void) printf(" %s", routename(sa)); + ADVANCE(cp, sa); + } + (void) putchar('\n'); + (void) fflush(stdout); +} + +void +bprintf(fp, b, s) + register FILE *fp; + register int b; + register u_char *s; +{ + register int i; + int gotsome = 0; + + if (b == 0) + return; + while ((i = *s++) != 0) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + (void) putc(i, fp); + gotsome = 1; + for (; (i = *s) > 32; s++) + (void) putc(i, fp); + } else + while (*s > 32) + s++; + } + if (gotsome) + (void) putc('>', fp); +} + +int +keyword(cp) + char *cp; +{ + register struct keytab *kt = keywords; + + while (kt->kt_cp && strcmp(kt->kt_cp, cp)) + kt++; + return kt->kt_i; +} + +void +sodump(su, which) + register sup su; + char *which; +{ + switch (su->sa.sa_family) { + case AF_LINK: + (void) printf("%s: link %s; ", + which, link_ntoa(&su->sdl)); + break; + case AF_INET: + (void) printf("%s: inet %s; ", + which, inet_ntoa(su->sin.sin_addr)); + break; + } + (void) fflush(stdout); +} + +/* States*/ +#define VIRGIN 0 +#define GOTONE 1 +#define GOTTWO 2 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) + +void +sockaddr(addr, sa) + register char *addr; + register struct sockaddr *sa; +{ + register char *cp = (char *)sa; + int size = sa->sa_len; + char *cplim = cp + size; + register int byte = 0, state = VIRGIN, new = 0 /* foil gcc */; + + bzero(cp, size); + cp++; + do { + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == 0) + state |= END; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case GOTTWO | DIGIT: + *cp++ = byte; /*FALLTHROUGH*/ + case VIRGIN | DIGIT: + state = GOTONE; byte = new; continue; + case GOTONE | DIGIT: + state = GOTTWO; byte = new + (byte << 4); continue; + default: /* | DELIM */ + state = VIRGIN; *cp++ = byte; byte = 0; continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; /* FALLTHROUGH */ + case VIRGIN | END: + break; + } + break; + } while (cp < cplim); + sa->sa_len = cp - (char *)sa; +} diff --git a/network_cmds/rtadvd.tproj/advcap.c b/network_cmds/rtadvd.tproj/advcap.c new file mode 100644 index 0000000..33b6fae --- /dev/null +++ b/network_cmds/rtadvd.tproj/advcap.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */ + +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * remcap - routines for dealing with the remote host data base + * + * derived from termcap + */ +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include "pathnames.h" +#include "rtadvd_logging.h" + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif +#define MAXHOP 32 /* max number of tc= indirections */ + +#define tgetent agetent +#define tnchktc anchktc +#define tnamatch anamatch +#define tgetnum agetnum +#define tgetflag agetflag +#define tgetstr agetstr + +#if 0 +#define V_TERMCAP "REMOTE" +#define V_TERM "HOST" +#endif + +char *RM; + +/* + * termcap - routines for dealing with the terminal capability data base + * + * BUG: Should use a "last" pointer in tbuf, so that searching + * for capabilities alphabetically would not be a n**2/2 + * process when large numbers of capabilities are given. + * Note: If we add a last pointer now we will screw up the + * tc capability. We really should compile termcap. + * + * Essentially all the work here is scanning and decoding escapes + * in string capabilities. We don't use stdio because the editor + * doesn't, and because living w/o it is not hard. + */ + +static char *tbuf; +static int hopcount; /* detect infinite loops in termcap, init 0 */ + +static char *remotefile; + +extern char *conffile; + +int tgetent(char *, char *); +int getent(char *, char *, char *); +int tnchktc(void); +int tnamatch(char *); +static char *tskip(char *); +int64_t tgetnum(char *); +int tgetflag(char *); +char *tgetstr(char *, char **); +static char *tdecode(char *, char **); + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +int +tgetent(bp, name) + char *bp, *name; +{ + char *cp; + + remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; + return (getent(bp, name, cp)); +} + +int +getent(bp, name, cp) + char *bp, *name, *cp; +{ + int c; + int i = 0, cnt = 0; + char ibuf[BUFSIZ]; + int tf; + + tbuf = bp; + tf = 0; + /* + * TERMCAP can have one of two things in it. It can be the + * name of a file to use instead of /etc/termcap. In this + * case it better start with a "/". Or it can be an entry to + * use so we don't have to read the file. In this case it + * has to already have the newlines crunched out. + */ + if (cp && *cp) { + tf = open(RM = cp, O_RDONLY); + } + if (tf < 0) { + infolog("<%s> open: %s", __func__, strerror(errno)); + return (-2); + } + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, BUFSIZ); + if (cnt <= 0) { + close(tf); + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\') { + cp--; + continue; + } + break; + } + if (cp >= bp + BUFSIZ - 1) { + write(STDERR_FILENO, "Remcap entry too long\n", + 22); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (tnamatch(name)) { + close(tf); + return (tnchktc()); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +int +tnchktc() +{ + char *p, *q; + char tcname[16]; /* name of similar terminal */ + char tcbuf[BUFSIZ]; + char *holdtbuf = tbuf; + int l; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p < tbuf) { + write(STDERR_FILENO, "Bad remcap entry\n", 18); + return (0); + } + p++; + /* p now points to beginning of last field */ + if (p[0] != 't' || p[1] != 'c') + return (1); + strlcpy(tcname, p + 3, sizeof tcname); + q = tcname; + while (*q && *q != ':') + q++; + *q = 0; + if (++hopcount > MAXHOP) { + write(STDERR_FILENO, "Infinite tc= loop\n", 18); + return (0); + } + if (getent(tcbuf, tcname, remotefile) != 1) { + return (0); + } + for (q = tcbuf; *q++ != ':'; ) + ; + l = p - holdtbuf + strlen(q); + + /* check length before copying string below */ + if (l > BUFSIZ) { + write(STDERR_FILENO, "Remcap entry too long\n", 23); + q[BUFSIZ - (p-holdtbuf)] = 0; + } + strlcpy(p, q, p-tbuf); + tbuf = holdtbuf; + return (1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +int +tnamatch(np) + char *np; +{ + char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return (0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +tskip(bp) + char *bp; +{ + int dquote; + + dquote = 0; + while (*bp) { + switch (*bp) { + case ':': + if (!dquote) + goto breakbreak; + else + bp++; + break; + case '\\': + bp++; + if (isdigit(*bp)) { + while (isdigit(*bp++)) + ; + } else + bp++; + case '"': + dquote = (dquote ? 1 : 0); + bp++; + break; + default: + bp++; + break; + } + } +breakbreak: + if (*bp == ':') + bp++; + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +int64_t +tgetnum(id) + char *id; +{ + int64_t i; + int base; + char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (*bp == 0) + return (-1); + if (strncmp(bp, id, strlen(id)) != 0) + continue; + bp += strlen(id); + if (*bp == '@') + return (-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit(*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +int +tgetflag(id) + char *id; +{ + char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (strncmp(bp, id, strlen(id)) == 0) { + bp += strlen(id); + if (!*bp || *bp == ':') + return (1); + else if (*bp == '@') + return (0); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +tgetstr(id, area) + char *id, **area; +{ + char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (strncmp(bp, id, strlen(id)) != 0) + continue; + bp += strlen(id); + if (*bp == '@') + return (0); + if (*bp != '=') + continue; + bp++; + return (tdecode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +tdecode(str, area) + char *str; + char **area; +{ + char *cp; + int c; + char *dp; + int i; + char term; + + term = ':'; + cp = *area; +again: + if (*str == '"') { + term = '"'; + str++; + } + while ((c = *str++) && c != term) { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\""; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit(c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit(*str)); + } + break; + } + *cp++ = c; + } + if (c == term && term != ':') { + term = ':'; + goto again; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/network_cmds/rtadvd.tproj/advcap.h b/network_cmds/rtadvd.tproj/advcap.h new file mode 100644 index 0000000..1b42b40 --- /dev/null +++ b/network_cmds/rtadvd.tproj/advcap.h @@ -0,0 +1,45 @@ +/* $KAME: advcap.h,v 1.5 2003/06/09 05:40:54 t-momose Exp $ */ + +/* + * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */ + +#ifndef _ADVCAP_H_ +#define _ADVCAP_H_ + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +extern int agetent(char *, const char *); +extern int agetflag(const char *); +extern int64_t agetnum(const char *); +extern char *agetstr(const char *, char **); + +__END_DECLS + +#endif /* _ADVCAP_H_ */ diff --git a/network_cmds/rtadvd.tproj/config.c b/network_cmds/rtadvd.tproj/config.c new file mode 100644 index 0000000..3d6388d --- /dev/null +++ b/network_cmds/rtadvd.tproj/config.c @@ -0,0 +1,1313 @@ +/* + * Copyright (c) 2009-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/route.h> +#include <net/if_dl.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet/icmp6.h> +#include <netinet6/nd6.h> + +#include <arpa/inet.h> + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <search.h> +#include <stdlib.h> +#include <unistd.h> +#include <ifaddrs.h> +#include <stddef.h> + +#include "rtadvd.h" +#include "advcap.h" +#include "timer.h" +#include "if.h" +#include "config.h" + +static time_t prefix_timo = (60 * 120); /* 2 hours. + * XXX: should be configurable. */ +extern struct rainfo *ralist; + +static struct rtadvd_timer *prefix_timeout(void *); +static void makeentry(char *, size_t, int, char *); +static int getinet6sysctl(int); +static int encode_domain(char *, u_char *); + +void +getconfig(intface) + char *intface; +{ + int stat, i; + int rdnss_length; + int dnssl_length; + char tbuf[BUFSIZ]; + struct rainfo *rai; + long val; + int64_t val64; + char buf[BUFSIZ]; + char *bp = buf; + char *addr, *flagstr; + char *capport; + static int forwarding = -1; + +#define MUSTHAVE(var, cap) \ + do { \ + int64_t t; \ + if ((t = agetnum(cap)) < 0) { \ + fprintf(stderr, "rtadvd: need %s for interface %s\n", \ + cap, intface); \ + exit(1); \ + } \ + var = t; \ + } while (0) +#define MAYHAVE(var, cap, def) \ + do { \ + if ((var = agetnum(cap)) < 0) \ + var = def; \ + } while (0) + + if ((stat = agetent(tbuf, intface)) <= 0) { + memset(tbuf, 0, sizeof(tbuf)); + errorlog("<%s> %s isn't defined in the configuration file" + " or the configuration file doesn't exist." + " Treat it as default", + __func__, intface); + } + + ELM_MALLOC(rai, exit(1)); + rai->prefix.next = rai->prefix.prev = &rai->prefix; +#ifdef ROUTEINFO + rai->route.next = rai->route.prev = &rai->route; +#endif + rai->rdnss_list.next = rai->rdnss_list.prev = &rai->rdnss_list; + rai->dnssl_list.next = rai->dnssl_list.prev = &rai->dnssl_list; + + /* check if we are allowed to forward packets (if not determined) */ + if (forwarding < 0) { + if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0) + exit(1); + } + + /* get interface information */ + if (agetflag("nolladdr")) + rai->advlinkopt = 0; + else + rai->advlinkopt = 1; + if (rai->advlinkopt) { + if ((rai->sdl = if_nametosdl(intface)) == NULL) { + errorlog("<%s> can't get information of %s", + __func__, intface); + exit(1); + } + rai->ifindex = rai->sdl->sdl_index; + } else + rai->ifindex = if_nametoindex(intface); + strlcpy(rai->ifname, intface, sizeof(rai->ifname)); + if ((rai->phymtu = if_getmtu(intface)) == 0) { + rai->phymtu = IPV6_MMTU; + errorlog("<%s> can't get interface mtu of %s. Treat as %d", + __func__, intface, IPV6_MMTU); + } + + /* + * set router configuration variables. + */ + MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); + if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { + errorlog("<%s> maxinterval (%ld) on %s is invalid " + "(must be between %u and %u)", __func__, val, + intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); + exit(1); + } + rai->maxinterval = (u_int)val; + MAYHAVE(val, "mininterval", rai->maxinterval/3); + if (val < MIN_MININTERVAL || val > (rai->maxinterval * 3) / 4) { + errorlog("<%s> mininterval (%ld) on %s is invalid " + "(must be between %d and %d)", + __func__, val, intface, MIN_MININTERVAL, + (rai->maxinterval * 3) / 4); + exit(1); + } + rai->mininterval = (u_int)val; + + MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); + rai->hoplimit = val & 0xff; + + if ((flagstr = (char *)agetstr("raflags", &bp))) { + val = 0; + if (strchr(flagstr, 'm')) + val |= ND_RA_FLAG_MANAGED; + if (strchr(flagstr, 'o')) + val |= ND_RA_FLAG_OTHER; + if (strchr(flagstr, 'h')) + val |= ND_RA_FLAG_RTPREF_HIGH; + if (strchr(flagstr, 'l')) { + if ((val & ND_RA_FLAG_RTPREF_HIGH)) { + errorlog("<%s> the \'h\' and \'l\'" + " router flags are exclusive", __func__); + exit(1); + } + val |= ND_RA_FLAG_RTPREF_LOW; + } + } else { + MAYHAVE(val, "raflags", 0); + } + rai->managedflg = val & ND_RA_FLAG_MANAGED; + rai->otherflg = val & ND_RA_FLAG_OTHER; +#ifndef ND_RA_FLAG_RTPREF_MASK +#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ +#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ +#endif + rai->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rai->rtpref == ND_RA_FLAG_RTPREF_RSV) { + errorlog("<%s> invalid router preference (%02x) on %s", + __func__, rai->rtpref, intface); + exit(1); + } + + MAYHAVE(val, "rltime", rai->maxinterval * 3); + if (val && (val < rai->maxinterval || val > MAXROUTERLIFETIME)) { + errorlog("<%s> router lifetime (%ld) on %s is invalid " + "(must be 0 or between %d and %d)", + __func__, val, intface, + rai->maxinterval, + MAXROUTERLIFETIME); + exit(1); + } + /* + * Basically, hosts MUST NOT send Router Advertisement messages at any + * time (RFC 2461, Section 6.2.3). However, it would sometimes be + * useful to allow hosts to advertise some parameters such as prefix + * information and link MTU. Thus, we allow hosts to invoke rtadvd + * only when router lifetime (on every advertising interface) is + * explicitly set zero. (see also the above section) + */ + if (val && forwarding == 0) { + errorlog("<%s> non zero router lifetime is specified for %s, " + "which must not be allowed for hosts. you must " + "change router lifetime or enable IPv6 forwarding.", + __func__, intface); + exit(1); + } + rai->lifetime = val & 0xffff; + + MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); + if (val < 0 || val > MAXREACHABLETIME) { + errorlog("<%s> reachable time (%ld) on %s is invalid " + "(must be no greater than %d)", + __func__, val, intface, MAXREACHABLETIME); + exit(1); + } + rai->reachabletime = (u_int32_t)val; + + MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); + if (val64 < 0 || val64 > 0xffffffff) { + errorlog("<%s> retrans time (%lld) on %s out of range", + __func__, (long long)val64, intface); + exit(1); + } + rai->retranstimer = (u_int32_t)val64; + + if (agetnum("hapref") != -1 || agetnum("hatime") != -1) { + errorlog("<%s> mobile-ip6 configuration not supported", + __func__); + exit(1); + } + /* prefix information */ + + /* + * This is an implementation specific parameter to consider + * link propagation delays and poorly synchronized clocks when + * checking consistency of advertised lifetimes. + */ + MAYHAVE(val, "clockskew", 0); + rai->clockskew = val; + + rai->pfxs = 0; + for (i = -1; i < MAXPREFIX; i++) { + struct prefix *pfx; + char entbuf[256]; + + makeentry(entbuf, sizeof(entbuf), i, "addr"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + /* allocate memory to store prefix information */ + ELM_MALLOC(pfx, exit(1)); + + pfx->rainfo = rai; + pfx->origin = PREFIX_FROM_CONFIG; + + if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) { + errorlog("<%s> inet_pton failed for %s", + __func__, addr); + exit(1); + } + if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) { + errorlog("<%s> multicast prefix (%s) must " + "not be advertised on %s", + __func__, addr, intface); + exit(1); + } + if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix)) + noticelog("<%s> link-local prefix (%s) will be" + " advertised on %s", + __func__, addr, intface); + + makeentry(entbuf, sizeof(entbuf), i, "prefixlen"); + MAYHAVE(val, entbuf, 64); + if (val < 0 || val > 128) { + errorlog("<%s> prefixlen (%ld) for %s " + "on %s out of range", + __func__, val, addr, intface); + exit(1); + } + pfx->prefixlen = (int)val; + + makeentry(entbuf, sizeof(entbuf), i, "pinfoflags"); + if ((flagstr = (char *)agetstr(entbuf, &bp))) { + val = 0; + if (strchr(flagstr, 'l')) + val |= ND_OPT_PI_FLAG_ONLINK; + if (strchr(flagstr, 'a')) + val |= ND_OPT_PI_FLAG_AUTO; + } else { + MAYHAVE(val, entbuf, + (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO)); + } + pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; + pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO; + + makeentry(entbuf, sizeof(entbuf), i, "vltime"); + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + errorlog("<%s> vltime (%lld) for " + "%s/%d on %s is out of range", + __func__, (long long)val64, + addr, pfx->prefixlen, intface); + exit(1); + } + pfx->validlifetime = (u_int32_t)val64; + + makeentry(entbuf, sizeof(entbuf), i, "vltimedecr"); + if (agetflag(entbuf)) { + struct timeval now; + gettimeofday(&now, 0); + pfx->vltimeexpire = + now.tv_sec + pfx->validlifetime; + } + + makeentry(entbuf, sizeof(entbuf), i, "pltime"); + MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + errorlog("<%s> pltime (%lld) for %s/%d on %s " + "is out of range", + __func__, (long long)val64, + addr, pfx->prefixlen, intface); + exit(1); + } + pfx->preflifetime = (u_int32_t)val64; + + makeentry(entbuf, sizeof(entbuf), i, "pltimedecr"); + if (agetflag(entbuf)) { + struct timeval now; + gettimeofday(&now, 0); + pfx->pltimeexpire = + now.tv_sec + pfx->preflifetime; + } + /* link into chain */ + insque(pfx, &rai->prefix); + rai->pfxs++; + } + if (rai->pfxs == 0) + get_prefix(rai); + + MAYHAVE(val, "mtu", 0); + if (val < 0 || val > 0xffffffff) { + errorlog("<%s> mtu (%ld) on %s out of range", + __func__, val, intface); + exit(1); + } + rai->linkmtu = (u_int32_t)val; + if (rai->linkmtu == 0) { + char *mtustr; + + if ((mtustr = (char *)agetstr("mtu", &bp)) && + strcmp(mtustr, "auto") == 0) + rai->linkmtu = rai->phymtu; + } + else if (rai->linkmtu < IPV6_MMTU || rai->linkmtu > rai->phymtu) { + errorlog("<%s> advertised link mtu (%lu) on %s is invalid (must " + "be between least MTU (%d) and physical link MTU (%d)", + __func__, (unsigned long)rai->linkmtu, intface, + IPV6_MMTU, rai->phymtu); + exit(1); + } + +#ifdef SIOCSIFINFO_IN6 + { + struct in6_ndireq ndi; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + errorlog("<%s> socket: %s", __func__, + strerror(errno)); + exit(1); + } + memset(&ndi, 0, sizeof(ndi)); + strlcpy(ndi.ifname, intface, sizeof(ndi.ifname)); + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) { + infolog("<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s", + __func__, intface, strerror(errno)); + } + + /* reflect the RA info to the host variables in kernel */ + ndi.ndi.chlim = rai->hoplimit; + ndi.ndi.retrans = rai->retranstimer; + ndi.ndi.basereachable = rai->reachabletime; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) { + infolog("<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s", + __func__, intface, strerror(errno)); + } + close(s); + } +#endif + + /* route information */ +#ifdef ROUTEINFO + rai->routes = 0; + for (i = -1; i < MAXROUTE; i++) { + struct rtinfo *rti; + char entbuf[256], oentbuf[256]; + + makeentry(entbuf, sizeof(entbuf), i, "rtprefix"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix"); + addr = (char *)agetstr(oentbuf, &bp); + if (addr) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } + } + if (addr == NULL) + continue; + + /* allocate memory to store prefix information */ + ELM_MALLOC(rti, exit(1)); + + /* link into chain */ + insque(rti, &rai->route); + rai->routes++; + + if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { + errorlog( "<%s> inet_pton failed for %s", + __func__, addr); + exit(1); + } +#if 0 + /* + * XXX: currently there's no restriction in route information + * prefix according to + * draft-ietf-ipngwg-router-selection-00.txt. + * However, I think the similar restriction be necessary. + */ + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { + errorlog("<%s> multicast route (%s) must " + "not be advertised on %s", + __func__, addr, intface); + exit(1); + } + if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { + noticelog("<%s> link-local route (%s) will " + "be advertised on %s", + __func__, addr, intface); + exit(1); + } +#endif + + makeentry(entbuf, sizeof(entbuf), i, "rtplen"); + /* XXX: 256 is a magic number for compatibility check. */ + MAYHAVE(val, entbuf, 256); + if (val == 256) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen"); + MAYHAVE(val, oentbuf, 256); + if (val != 256) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } else + val = 64; + } + if (val < 0 || val > 128) { + errorlog("<%s> prefixlen (%ld) for %s on %s " + "out of range", + __func__, val, addr, intface); + exit(1); + } + rti->prefixlen = (int)val; + + makeentry(entbuf, sizeof(entbuf), i, "rtflags"); + if ((flagstr = (char *)agetstr(entbuf, &bp))) { + val = 0; + if (strchr(flagstr, 'h')) + val |= ND_RA_FLAG_RTPREF_HIGH; + if (strchr(flagstr, 'l')) { + if ((val & ND_RA_FLAG_RTPREF_HIGH)) { + errorlog( + "<%s> the \'h\' and \'l\' route" + " preferences are exclusive", + __func__); + exit(1); + } + val |= ND_RA_FLAG_RTPREF_LOW; + } + } else + MAYHAVE(val, entbuf, 256); /* XXX */ + if (val == 256) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags"); + MAYHAVE(val, oentbuf, 256); + if (val != 256) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } else + val = 0; + } + rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { + errorlog("<%s> invalid route preference (%02x) " + "for %s/%d on %s", + __func__, rti->rtpref, addr, + rti->prefixlen, intface); + exit(1); + } + + /* + * Since the spec does not a default value, we should make + * this entry mandatory. However, FreeBSD 4.4 has shipped + * with this field being optional, we use the router lifetime + * as an ad-hoc default value with a warning message. + */ + makeentry(entbuf, sizeof(entbuf), i, "rtltime"); + MAYHAVE(val64, entbuf, -1); + if (val64 == -1) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime"); + MAYHAVE(val64, oentbuf, -1); + if (val64 != -1) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } else { + fprintf(stderr, "%s should be specified " + "for interface %s.\n", + entbuf, intface); + val64 = rai->lifetime; + } + } + if (val64 < 0 || val64 > 0xffffffff) { + errorlog( "<%s> route lifetime (%lld) for " + "%s/%d on %s out of range", __func__, + (long long)val64, addr, rti->prefixlen, intface); + exit(1); + } + rti->ltime = (u_int32_t)val64; + } +#endif + + /* RDNSS option (RFC5006) */ + MAYHAVE(val, "rdnsslifetime", 2 * rai->maxinterval); + if (val < rai->maxinterval || val > (2 * rai->maxinterval)) { + noticelog("<%s> rdnsslifetime (%lu) on %s SHOULD " + "be between %u and %u", __func__, val, + intface, rai->maxinterval, 2 * rai->maxinterval); + } + rai->rdnss_lifetime = val; + if ((rdnss_length = agetnum("rdnssaddrs")) < 0) { + rai->rdnss_length = 0; + } + else { + rai->rdnss_length = rdnss_length; + + /* traverse in reverse order so that the queue has correct order */ + for (i = (rdnss_length - 1); i >= 0; i--) { + struct rdnss *rdnss; + char entbuf[256]; + + /* allocate memory to store server address information */ + ELM_MALLOC(rdnss, exit(1)); + /* link into chain */ + insque(rdnss, &rai->rdnss_list); + + makeentry(entbuf, sizeof(entbuf), i, "rdnssaddr"); + addr = (char *)agetstr(entbuf, &bp); + + if (addr == NULL && rdnss_length == 1) { + makeentry(entbuf, sizeof(entbuf), -1, "rdnssaddr"); + addr = agetstr(entbuf, &bp); + } + + if (addr == NULL) { + errorlog("<%s> need %s as a DNS server address for " + "interface %s", + __func__, entbuf, intface); + exit(1); + } + + if (inet_pton(AF_INET6, addr, &rdnss->addr) != 1) { + errorlog("<%s> inet_pton failed for %s", + __func__, addr); + exit(1); + } + if (IN6_IS_ADDR_MULTICAST(&rdnss->addr)) { + errorlog("<%s> multicast address (%s) must " + "not be advertised as recursive DNS server", + __func__, addr); + exit(1); + } + } + } + + /* DNSSL option (RFC6106) */ + + /* Parse the DNSSL lifetime from the config */ + MAYHAVE(val, "dnssllifetime", 2 * rai->maxinterval); + if (val < rai->maxinterval || val > (2 * rai->maxinterval)) { + noticelog("<%s> dnssllifetime (%lu) on %s SHOULD " + "be between %u and %u", __func__, val, + intface, rai->maxinterval, 2 * rai->maxinterval); + } + rai->dnssl_lifetime = val; + rai->dnssl_option_length = 8; /* 8 bytes for the option header */ + + /* Parse the DNSSL domain list from the config */ + if ((dnssl_length = agetnum("dnssldomains")) < 0) { + rai->dnssl_length = 0; + } else { + rai->dnssl_length = dnssl_length; + + for (i = (rai->dnssl_length - 1); i >= 0; i--) { + unsigned char *dnssl_buf; + struct dnssl *dnssl; + int dnssl_len; + char entbuf[sizeof("dnssldomain") + 20]; + char *domain; + int domain_len; + + makeentry(entbuf, sizeof(entbuf), i, "dnssldomain"); + domain = agetstr(entbuf, &bp); + + if (domain == NULL && rai->dnssl_length == 1) { + makeentry(entbuf, sizeof(entbuf), -1, "dnssldomain"); + domain = agetstr(entbuf, &bp); + } + + if (domain == NULL) { + errorlog("<%s> need %s as a DNS search domain for " + "interface %s", + __func__, entbuf, intface); + exit(1); + } + + domain_len = strlen(domain); + + /* Trim off leading dots */ + while (domain_len > 0 && domain[0] == '.') { + domain++; + domain_len--; + } + + /* Trim off trailing dots */ + while (domain_len > 0 && domain[domain_len-1] == '.') { + domain_len--; + } + + if (domain_len > 0) { + dnssl_len = sizeof(struct dnssl) + domain_len + 1; + dnssl_buf = (unsigned char *)malloc(dnssl_len); + + memset(dnssl_buf, 0, dnssl_len); + + dnssl = (struct dnssl *)dnssl_buf; + insque(dnssl, &rai->dnssl_list); + + /* Copy the domain name in at the end of the dnssl struct */ + memcpy(dnssl_buf + offsetof(struct dnssl, domain), domain, + domain_len); + + /* Add 2 for leading length byte and the trailing 0 byte */ + rai->dnssl_option_length += domain_len + 2; + } + } + + /* Round up to the next multiple of 8 */ + rai->dnssl_option_length += (8 - (rai->dnssl_option_length & 0x7)); + } + + /* captive portal */ + capport = agetstr("capport", &bp); + if (capport != NULL) { + rai->capport = strdup(capport); + rai->capport_length = strlen(capport); + rai->capport_option_length + = sizeof(struct nd_opt_hdr) + rai->capport_length; + rai->capport_option_length + += (8 - (rai->capport_option_length & 0x7)); + } + + /* okey */ + rai->next = ralist; + ralist = rai; + + /* construct the sending packet */ + make_packet(rai); + + /* set timer */ + rai->timer = rtadvd_add_timer(ra_timeout, ra_timer_update, + rai, rai); + ra_timer_update((void *)rai, &rai->timer->tm); + rtadvd_set_timer(&rai->timer->tm, rai->timer); +} + +void +get_prefix(struct rainfo *rai) +{ + struct ifaddrs *ifap, *ifa; + struct prefix *pfx; + struct in6_addr *a; + u_char *p, *ep, *m, *lim; + char ntopbuf[INET6_ADDRSTRLEN]; + + if (getifaddrs(&ifap) < 0) { + errorlog( + "<%s> can't get interface addresses", + __func__); + exit(1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + int plen; + + if (strcmp(ifa->ifa_name, rai->ifname) != 0) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL(a)) + continue; + /* get prefix length */ + m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; + plen = prefixlen(m, lim); + if (plen <= 0 || plen > 128) { + errorlog( "<%s> failed to get prefixlen " + "or prefix is invalid", + __func__); + exit(1); + } + if (plen == 128) /* XXX */ + continue; + if (find_prefix(rai, a, plen)) { + /* ignore a duplicated prefix. */ + continue; + } + + /* allocate memory to store prefix info. */ + ELM_MALLOC(pfx, exit(1)); + /* set prefix, sweep bits outside of prefixlen */ + pfx->prefixlen = plen; + memcpy(&pfx->prefix, a, sizeof(*a)); + p = (u_char *)&pfx->prefix; + ep = (u_char *)(&pfx->prefix + 1); + while (m < lim && p < ep) + *p++ &= *m++; + while (p < ep) + *p++ = 0x00; + if (!inet_ntop(AF_INET6, &pfx->prefix, ntopbuf, + sizeof(ntopbuf))) { + errorlog("<%s> inet_ntop failed", __func__); + exit(1); + } + debuglog("<%s> add %s/%d to prefix list on %s", + __func__, ntopbuf, pfx->prefixlen, rai->ifname); + + /* set other fields with protocol defaults */ + pfx->validlifetime = DEF_ADVVALIDLIFETIME; + pfx->preflifetime = DEF_ADVPREFERREDLIFETIME; + pfx->onlinkflg = 1; + pfx->autoconfflg = 1; + pfx->origin = PREFIX_FROM_KERNEL; + pfx->rainfo = rai; + + /* link into chain */ + insque(pfx, &rai->prefix); + + /* counter increment */ + rai->pfxs++; + } + + freeifaddrs(ifap); +} + +static void +makeentry(buf, len, id, string) + char *buf; + size_t len; + int id; + char *string; +{ + + if (id < 0) + strlcpy(buf, string, len); + else + snprintf(buf, len, "%s%d", string, id); +} + +/* + * Add a prefix to the list of specified interface and reconstruct + * the outgoing packet. + * The prefix must not be in the list. + * XXX: other parameters of the prefix (e.g. lifetime) should be + * able to be specified. + */ +static void +add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) +{ + struct prefix *prefix; + char ntopbuf[INET6_ADDRSTRLEN]; + + ELM_MALLOC(prefix, exit(1)); + prefix->prefix = ipr->ipr_prefix.sin6_addr; + prefix->prefixlen = ipr->ipr_plen; + prefix->validlifetime = ipr->ipr_vltime; + prefix->preflifetime = ipr->ipr_pltime; + prefix->onlinkflg = ipr->ipr_raf_onlink; + prefix->autoconfflg = ipr->ipr_raf_auto; + prefix->origin = PREFIX_FROM_DYNAMIC; + prefix->rainfo = rai; + + insque(prefix, &rai->prefix); + + debuglog("<%s> new prefix %s/%d was added on %s", + __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + ipr->ipr_plen, rai->ifname); + + /* free the previous packet */ + free(rai->ra_data); + rai->ra_data = NULL; + + /* reconstruct the packet */ + rai->pfxs++; + make_packet(rai); +} + +/* + * Delete a prefix to the list of specified interface and reconstruct + * the outgoing packet. + * The prefix must be in the list. + */ +void +delete_prefix(struct prefix *prefix) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + struct rainfo *rai = prefix->rainfo; + + remque(prefix); + debuglog("<%s> prefix %s/%d was deleted on %s", + __func__, inet_ntop(AF_INET6, &prefix->prefix, + ntopbuf, INET6_ADDRSTRLEN), + prefix->prefixlen, rai->ifname); + if (prefix->timer) + rtadvd_remove_timer(&prefix->timer); + free(prefix); + rai->pfxs--; +} + +void +invalidate_prefix(struct prefix *prefix) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + struct timeval timo; + struct rainfo *rai = prefix->rainfo; + + if (prefix->timer) { /* sanity check */ + errorlog("<%s> assumption failure: timer already exists", + __func__); + exit(1); + } + + debuglog("<%s> prefix %s/%d was invalidated on %s, " + "will expire in %ld seconds", __func__, + inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN), + prefix->prefixlen, rai->ifname, (long)prefix_timo); + + /* set the expiration timer */ + prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL); + if (prefix->timer == NULL) { + errorlog("<%s> failed to add a timer for a prefix. " + "remove the prefix", __func__); + delete_prefix(prefix); + return; + } + timo.tv_sec = prefix_timo; + timo.tv_usec = 0; + rtadvd_set_timer(&timo, prefix->timer); +} + +static struct rtadvd_timer * +prefix_timeout(void *arg) +{ + struct prefix *prefix = (struct prefix *)arg; + + delete_prefix(prefix); + + return(NULL); +} + +void +update_prefix(struct prefix * prefix) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + struct rainfo *rai = prefix->rainfo; + + if (prefix->timer == NULL) { /* sanity check */ + errorlog("<%s> assumption failure: timer does not exist", + __func__); + exit(1); + } + + debuglog("<%s> prefix %s/%d was re-enabled on %s", + __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, + INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname); + + /* stop the expiration timer */ + rtadvd_remove_timer(&prefix->timer); +} + +/* + * Try to get an in6_prefixreq contents for a prefix which matches + * ipr->ipr_prefix and ipr->ipr_plen and belongs to + * the interface whose name is ipr->ipr_name[]. + */ +static int +init_prefix(struct in6_prefixreq *ipr) +{ +#if 0 + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + errorlog("<%s> socket: %s", __func__, + strerror(errno)); + exit(1); + } + + if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) { + infolog("<%s> ioctl:SIOCGIFPREFIX %s", __func__, + strerror(errno)); + + ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; + ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; + ipr->ipr_raf_onlink = 1; + ipr->ipr_raf_auto = 1; + /* omit other field initialization */ + } + else if (ipr->ipr_origin < PR_ORIG_RR) { + char ntopbuf[INET6_ADDRSTRLEN]; + + noticelog("<%s> Added prefix(%s)'s origin %d is" + "lower than PR_ORIG_RR(router renumbering)." + "This should not happen if I am router", __func__, + inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, + sizeof(ntopbuf)), ipr->ipr_origin); + close(s); + return 1; + } + + close(s); + return 0; +#else + ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; + ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; + ipr->ipr_raf_onlink = 1; + ipr->ipr_raf_auto = 1; + return 0; +#endif +} + +void +make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) +{ + struct in6_prefixreq ipr; + + memset(&ipr, 0, sizeof(ipr)); + if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { + errorlog("<%s> Prefix added interface No.%d doesn't" + "exist. This should not happen! %s", __func__, + ifindex, strerror(errno)); + exit(1); + } + ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix); + ipr.ipr_prefix.sin6_family = AF_INET6; + ipr.ipr_prefix.sin6_addr = *addr; + ipr.ipr_plen = plen; + + if (init_prefix(&ipr)) + return; /* init failed by some error */ + add_prefix(rai, &ipr); +} + +void +make_packet(struct rainfo *rainfo) +{ + size_t packlen, lladdroptlen = 0; + u_char *buf; + struct nd_router_advert *ra; + struct nd_opt_prefix_info *ndopt_pi; + struct nd_opt_mtu *ndopt_mtu; +#ifdef ROUTEINFO + struct nd_opt_route_info *ndopt_rti; + struct rtinfo *rti; +#endif + struct prefix *pfx; + + /* calculate total length */ + packlen = sizeof(struct nd_router_advert); + if (rainfo->advlinkopt) { + if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) { + infolog("<%s> link-layer address option has" + " null length on %s. Treat as not included.", + __func__, rainfo->ifname); + rainfo->advlinkopt = 0; + } + packlen += lladdroptlen; + } + if (rainfo->pfxs) + packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; + if (rainfo->linkmtu) + packlen += sizeof(struct nd_opt_mtu); +#ifdef ROUTEINFO + for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) + packlen += sizeof(struct nd_opt_route_info) + + ((rti->prefixlen + 0x3f) >> 6) * 8; +#endif + if (rainfo->rdnss_length > 0) + packlen += 8 + sizeof(struct in6_addr) * rainfo->rdnss_length; + + if (rainfo->dnssl_length > 0) { + packlen += rainfo->dnssl_option_length; + } + if (rainfo->capport_option_length != 0) { + packlen += rainfo->capport_option_length; + } + + /* allocate memory for the packet */ + if ((buf = malloc(packlen)) == NULL) { + errorlog("<%s> can't get enough memory for an RA packet", + __func__); + exit(1); + } + if (rainfo->ra_data) { + /* free the previous packet */ + free(rainfo->ra_data); + rainfo->ra_data = NULL; + } + rainfo->ra_data = buf; + /* XXX: what if packlen > 576? */ + rainfo->ra_datalen = packlen; + + /* + * construct the packet + */ + ra = (struct nd_router_advert *)buf; + ra->nd_ra_type = ND_ROUTER_ADVERT; + ra->nd_ra_code = 0; + ra->nd_ra_cksum = 0; + ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit); + ra->nd_ra_flags_reserved = 0; /* just in case */ + /* + * XXX: the router preference field, which is a 2-bit field, should be + * initialized before other fields. + */ + ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref; + ra->nd_ra_flags_reserved |= + rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; + ra->nd_ra_flags_reserved |= + rainfo->otherflg ? ND_RA_FLAG_OTHER : 0; + ra->nd_ra_router_lifetime = htons(rainfo->lifetime); + ra->nd_ra_reachable = htonl(rainfo->reachabletime); + ra->nd_ra_retransmit = htonl(rainfo->retranstimer); + buf += sizeof(*ra); + + if (rainfo->advlinkopt) { + lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf); + buf += lladdroptlen; + } + + if (rainfo->linkmtu) { + ndopt_mtu = (struct nd_opt_mtu *)buf; + ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; + ndopt_mtu->nd_opt_mtu_len = 1; + ndopt_mtu->nd_opt_mtu_reserved = 0; + ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu); + buf += sizeof(struct nd_opt_mtu); + } + + for (pfx = rainfo->prefix.next; + pfx != &rainfo->prefix; pfx = pfx->next) { + u_int32_t vltime, pltime; + struct timeval now; + + ndopt_pi = (struct nd_opt_prefix_info *)buf; + ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + ndopt_pi->nd_opt_pi_len = 4; + ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen; + ndopt_pi->nd_opt_pi_flags_reserved = 0; + if (pfx->onlinkflg) + ndopt_pi->nd_opt_pi_flags_reserved |= + ND_OPT_PI_FLAG_ONLINK; + if (pfx->autoconfflg) + ndopt_pi->nd_opt_pi_flags_reserved |= + ND_OPT_PI_FLAG_AUTO; + if (pfx->timer) + vltime = 0; + else { + if (pfx->vltimeexpire || pfx->pltimeexpire) + gettimeofday(&now, NULL); + if (pfx->vltimeexpire == 0) + vltime = pfx->validlifetime; + else + vltime = (pfx->vltimeexpire > now.tv_sec) ? + pfx->vltimeexpire - now.tv_sec : 0; + } + if (pfx->timer) + pltime = 0; + else { + if (pfx->pltimeexpire == 0) + pltime = pfx->preflifetime; + else + pltime = (pfx->pltimeexpire > now.tv_sec) ? + pfx->pltimeexpire - now.tv_sec : 0; + } + if (vltime < pltime) { + /* + * this can happen if vltime is decrement but pltime + * is not. + */ + pltime = vltime; + } + ndopt_pi->nd_opt_pi_valid_time = htonl(vltime); + ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime); + ndopt_pi->nd_opt_pi_reserved2 = 0; + ndopt_pi->nd_opt_pi_prefix = pfx->prefix; + + buf += sizeof(struct nd_opt_prefix_info); + } + +#ifdef ROUTEINFO + for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) { + u_int8_t psize = (rti->prefixlen + 0x3f) >> 6; + + ndopt_rti = (struct nd_opt_route_info *)buf; + ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; + ndopt_rti->nd_opt_rti_len = 1 + psize; + ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen; + ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref; + ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime); + memcpy(ndopt_rti + 1, &rti->prefix, psize * 8); + buf += sizeof(struct nd_opt_route_info) + psize * 8; + } +#endif + + if (rainfo->rdnss_length > 0) { + struct nd_opt_rdnss * ndopt_rdnss; + struct rdnss * rdnss; + + ndopt_rdnss = (struct nd_opt_rdnss*) buf; + ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; + ndopt_rdnss->nd_opt_rdnss_len = 1 + (rainfo->rdnss_length * 2); + ndopt_rdnss->nd_opt_rdnss_reserved = 0; + ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rainfo->rdnss_lifetime); + buf += 8; + + for (rdnss = rainfo->rdnss_list.next; + rdnss != &rainfo->rdnss_list; + rdnss = rdnss->next) + { + struct in6_addr* addr6 = (struct in6_addr*) buf; + *addr6 = rdnss->addr; + buf += sizeof *addr6; + } + } + + if (rainfo->dnssl_length > 0) { + struct nd_opt_dnssl * dnssl_opt; + struct dnssl * dnssl; + int domains_length = 0; + u_char * cursor = buf; + + memset(cursor, 0, rainfo->dnssl_option_length); + + dnssl_opt = (struct nd_opt_dnssl *)cursor; + dnssl_opt->nd_opt_dnssl_type = ND_OPT_DNSSL; + /* + * Length is in units of 8 octets. Divide total byte length + * of the option by 8. + */ + dnssl_opt->nd_opt_dnssl_len = rainfo->dnssl_option_length >> 3; + dnssl_opt->nd_opt_dnssl_reserved = 0; + dnssl_opt->nd_opt_dnssl_lifetime = + htonl(rainfo->dnssl_lifetime); + + cursor += offsetof(struct nd_opt_dnssl, nd_opt_dnssl_domains); + + for (dnssl = rainfo->dnssl_list.next; + dnssl != &rainfo->dnssl_list; + dnssl = dnssl->next) + { + int encodeLen = encode_domain(dnssl->domain, cursor); + cursor += encodeLen; + domains_length += encodeLen; + } + + buf += rainfo->dnssl_option_length; + } + if (rainfo->capport != NULL) { + struct nd_opt_hdr * capport_opt; + u_int32_t zero_space; + + capport_opt = (struct nd_opt_hdr *)buf; +#ifndef ND_OPT_CAPTIVE_PORTAL +#define ND_OPT_CAPTIVE_PORTAL 37 /* RFC 7710 */ +#endif /* ND_OPT_CAPTIVE_PORTAL */ + capport_opt->nd_opt_type = ND_OPT_CAPTIVE_PORTAL; + capport_opt->nd_opt_len = rainfo->capport_option_length >> 3; + buf += sizeof(*capport_opt); + bcopy(rainfo->capport, buf, rainfo->capport_length); + buf += rainfo->capport_length; + zero_space = rainfo->capport_option_length + - rainfo->capport_length; + if (zero_space > 0) { + bzero(buf, zero_space); + buf += zero_space; + } + } + return; +} + +static int +getinet6sysctl(int code) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; + int value; + size_t size; + + mib[3] = code; + size = sizeof(value); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) + < 0) { + errorlog( "<%s>: failed to get ip6 sysctl(%d): %s", + __func__, code, + strerror(errno)); + return(-1); + } + else + return(value); +} + +/* + * Encode a domain name into a buffer according to the rules in RFC 1035 section + * 3.1. Do not use the compression techniques outlined in section 4.1.4. + */ +int +encode_domain(char *domain, u_char *dst) +{ + char *domainCopy = strdup(domain); + char *input = domainCopy; + char *label; + u_char *cursor = dst; + + while ((label = strsep(&input, ".")) != NULL) { + int label_len = strlen(label) & 0x3f; /* Max length is 63 */ + if (label_len > 0) { + *cursor = (u_char)label_len; + cursor++; + memcpy(cursor, label, label_len); + cursor += label_len; + } + } + *cursor = 0; + cursor++; + + free(domainCopy); + + return (cursor - dst); +} diff --git a/network_cmds/rtadvd.tproj/config.h b/network_cmds/rtadvd.tproj/config.h new file mode 100644 index 0000000..f1f0387 --- /dev/null +++ b/network_cmds/rtadvd.tproj/config.h @@ -0,0 +1,46 @@ +/* $KAME: config.h,v 1.8 2003/06/17 08:26:22 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void getconfig(char *); +extern void delete_prefix(struct prefix *); +extern void invalidate_prefix(struct prefix *); +extern void update_prefix(struct prefix *); +extern void make_prefix(struct rainfo *, int, struct in6_addr *, int); +extern void make_packet(struct rainfo *); +extern void get_prefix(struct rainfo *); + + +/* + * it is highly unlikely to have 100 prefix information options, + * so it should be okay to limit it + */ +#define MAXPREFIX 100 +#define MAXROUTE 100 diff --git a/network_cmds/rtadvd.tproj/dump.c b/network_cmds/rtadvd.tproj/dump.c new file mode 100644 index 0000000..ea5dcd9 --- /dev/null +++ b/network_cmds/rtadvd.tproj/dump.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: dump.c,v 1.32 2003/05/19 09:46:50 keiichi Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> + +#include <netinet/in.h> + +/* XXX: the following two are non-standard include files */ +#include <netinet6/in6_var.h> +#include <netinet6/nd6.h> + +#include <arpa/inet.h> + +#include <time.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +#include "rtadvd.h" +#include "timer.h" +#include "if.h" +#include "dump.h" + +static FILE *fp; + +extern struct rainfo *ralist; + +static char *ether_str(struct sockaddr_dl *); +static void if_dump(void); + +static char *rtpref_str[] = { + "medium", /* 00 */ + "high", /* 01 */ + "rsv", /* 10 */ + "low" /* 11 */ +}; + +static char * +ether_str(sdl) + struct sockaddr_dl *sdl; +{ + static char hbuf[32]; + u_char *cp; + + if (sdl->sdl_alen && sdl->sdl_alen > 5) { + cp = (u_char *)LLADDR(sdl); + snprintf(hbuf, sizeof(hbuf), "%x:%x:%x:%x:%x:%x", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); + } else + snprintf(hbuf, sizeof(hbuf), "NONE"); + + return(hbuf); +} + +static void +if_dump() +{ + struct rainfo *rai; + struct prefix *pfx; +#ifdef ROUTEINFO + struct rtinfo *rti; +#endif + char prefixbuf[INET6_ADDRSTRLEN]; + int first; + struct timeval now; + + gettimeofday(&now, NULL); /* XXX: unused in most cases */ + for (rai = ralist; rai; rai = rai->next) { + fprintf(fp, "%s:\n", rai->ifname); + struct if_msghdr *ifm = get_interface_entry(rai->ifindex); + if (ifm == NULL) { + debuglog("Skippingg RA entry for interface %s" + "as we couldn't find interface entry for it.", rai->ifname); + continue; + } + fprintf(fp, " Status: %s\n", + (ifm->ifm_flags & IFF_UP) ? "UP" : + "DOWN"); + + /* control information */ + if (rai->lastsent.tv_sec) { + /* note that ctime() appends CR by itself */ + fprintf(fp, " Last RA sent: %s", + ctime((time_t *)&rai->lastsent.tv_sec)); + } + if (rai->timer) { + fprintf(fp, " Next RA will be sent: %s", + ctime((time_t *)&rai->timer->tm.tv_sec)); + } + else + fprintf(fp, " RA timer is stopped"); + fprintf(fp, " waits: %d, initcount: %d\n", + rai->waiting, rai->initcounter); + + /* statistics */ + fprintf(fp, " statistics: RA(out/in/inconsistent): " + "%llu/%llu/%llu, ", + (unsigned long long)rai->raoutput, + (unsigned long long)rai->rainput, + (unsigned long long)rai->rainconsistent); + fprintf(fp, "RS(input): %llu\n", + (unsigned long long)rai->rsinput); + + /* interface information */ + if (rai->advlinkopt) + fprintf(fp, " Link-layer address: %s\n", + ether_str(rai->sdl)); + fprintf(fp, " MTU: %d\n", rai->phymtu); + + /* Router configuration variables */ + fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, " + "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval, + rai->mininterval); + fprintf(fp, " Flags: %s%s%s, ", + rai->managedflg ? "M" : "", rai->otherflg ? "O" : "", ""); + fprintf(fp, "Preference: %s, ", + rtpref_str[(rai->rtpref >> 3) & 0xff]); + fprintf(fp, "MTU: %d\n", rai->linkmtu); + fprintf(fp, " ReachableTime: %d, RetransTimer: %d, " + "CurHopLimit: %d\n", rai->reachabletime, + rai->retranstimer, rai->hoplimit); + if (rai->clockskew) + fprintf(fp, " Clock skew: %ldsec\n", + rai->clockskew); + for (first = 1, pfx = rai->prefix.next; pfx != &rai->prefix; + pfx = pfx->next) { + if (first) { + fprintf(fp, " Prefixes:\n"); + first = 0; + } + fprintf(fp, " %s/%d(", + inet_ntop(AF_INET6, &pfx->prefix, prefixbuf, + sizeof(prefixbuf)), pfx->prefixlen); + switch (pfx->origin) { + case PREFIX_FROM_KERNEL: + fprintf(fp, "KERNEL, "); + break; + case PREFIX_FROM_CONFIG: + fprintf(fp, "CONFIG, "); + break; + case PREFIX_FROM_DYNAMIC: + fprintf(fp, "DYNAMIC, "); + break; + } + if (pfx->validlifetime == ND6_INFINITE_LIFETIME) + fprintf(fp, "vltime: infinity"); + else + fprintf(fp, "vltime: %ld", + (long)pfx->validlifetime); + if (pfx->vltimeexpire != 0) + fprintf(fp, "(decr,expire %ld), ", (long) + pfx->vltimeexpire > now.tv_sec ? + pfx->vltimeexpire - now.tv_sec : 0); + else + fprintf(fp, ", "); + if (pfx->preflifetime == ND6_INFINITE_LIFETIME) + fprintf(fp, "pltime: infinity"); + else + fprintf(fp, "pltime: %ld", + (long)pfx->preflifetime); + if (pfx->pltimeexpire != 0) + fprintf(fp, "(decr,expire %ld), ", (long) + pfx->pltimeexpire > now.tv_sec ? + pfx->pltimeexpire - now.tv_sec : 0); + else + fprintf(fp, ", "); + fprintf(fp, "flags: %s%s%s", + pfx->onlinkflg ? "L" : "", + pfx->autoconfflg ? "A" : "", + ""); + if (pfx->timer) { + struct timeval *rest; + + rest = rtadvd_timer_rest(pfx->timer); + if (rest) { /* XXX: what if not? */ + fprintf(fp, ", expire in: %ld", + (long)rest->tv_sec); + } + } + fprintf(fp, ")\n"); + } +#ifdef ROUTEINFO + for (first = 1, rti = rai->route.next; rti != &rai->route; + rti = rti->next) { + if (first) { + fprintf(fp, " Route Information:\n"); + first = 0; + } + fprintf(fp, " %s/%d (", + inet_ntop(AF_INET6, &rti->prefix, + prefixbuf, sizeof(prefixbuf)), + rti->prefixlen); + fprintf(fp, "preference: %s, ", + rtpref_str[0xff & (rti->rtpref >> 3)]); + if (rti->ltime == ND6_INFINITE_LIFETIME) + fprintf(fp, "lifetime: infinity"); + else + fprintf(fp, "lifetime: %ld", (long)rti->ltime); + fprintf(fp, ")\n"); + } +#endif + } +} + +void +rtadvd_dump_file(dumpfile) + char *dumpfile; +{ + debuglog("<%s> dump current status to %s", __func__, + dumpfile); + + if ((fp = fopen(dumpfile, "w")) == NULL) { + errorlog("<%s> open a dump file(%s)", + __func__, dumpfile); + return; + } + + if_dump(); + + fclose(fp); +} diff --git a/network_cmds/rtadvd.tproj/dump.h b/network_cmds/rtadvd.tproj/dump.h new file mode 100644 index 0000000..4e10b4a --- /dev/null +++ b/network_cmds/rtadvd.tproj/dump.h @@ -0,0 +1,32 @@ +/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void rtadvd_dump_file(char *); diff --git a/network_cmds/rtadvd.tproj/if.c b/network_cmds/rtadvd.tproj/if.c new file mode 100644 index 0000000..9862af2 --- /dev/null +++ b/network_cmds/rtadvd.tproj/if.c @@ -0,0 +1,604 @@ +/* $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/ethernet.h> +#include <ifaddrs.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "rtadvd.h" +#include "if.h" + +#define ROUNDUP(a, size) \ + (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) + +#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ + ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ + sizeof(uint32_t)) :\ + sizeof(uint32_t))) + +/* Interface list */ +TAILQ_HEAD(ifilist_head_t, ifinfo); +struct ifilist_head_t ifilist = TAILQ_HEAD_INITIALIZER(ifilist); +size_t ifblock_size; +char *ifblock; + +static void get_iflist(char **buf, size_t *size); +static void parse_iflist(char *buf, size_t bufsize); +static void purge_iflist(); + +static void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + NEXT_SA(sa); + } + else + rti_info[i] = NULL; + } +} + +struct sockaddr_dl * +if_nametosdl(char *name) +{ + int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + char *buf, *next, *lim; + size_t len; + struct if_msghdr *ifm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl = NULL, *ret_sdl = NULL; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return(NULL); + if ((buf = malloc(len)) == NULL) + return(NULL); + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + free(buf); + return(NULL); + } + + lim = buf + len; + for (next = buf; next < lim; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_IFINFO) { + sa = (struct sockaddr *)(ifm + 1); + get_rtaddrs(ifm->ifm_addrs, sa, rti_info); + if ((sa = rti_info[RTAX_IFP]) != NULL) { + if (sa->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)sa; + if (strlen(name) != sdl->sdl_nlen) + continue; /* not same len */ + if (strncmp(&sdl->sdl_data[0], + name, + sdl->sdl_nlen) == 0) { + break; + } + } + } + } + } + if (next == lim) { + /* search failed */ + free(buf); + return(NULL); + } + + if (sdl == NULL || (ret_sdl = malloc(sdl->sdl_len)) == NULL) + goto end; + memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); + +end: + free(buf); + return(ret_sdl); +} + +int +if_getmtu(char *name) +{ + struct ifaddrs *ifap, *ifa; + struct if_data *ifd; + int mtu = 0; + + if (getifaddrs(&ifap) < 0) + return(0); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, name) == 0) { + ifd = ifa->ifa_data; + if (ifd) + mtu = ifd->ifi_mtu; + break; + } + } + freeifaddrs(ifap); + +#ifdef SIOCGIFMTU /* XXX: this ifdef may not be necessary */ + if (mtu == 0) { + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + return(0); + + ifr.ifr_addr.sa_family = AF_INET6; + strlcpy(ifr.ifr_name, name, + sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) { + close(s); + return(0); + } + close(s); + + mtu = ifr.ifr_mtu; + } +#endif + + return(mtu); +} + +/* give interface index and its old flags, then new flags returned */ +int +if_getflags(int ifindex, int oifflags) +{ + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + errorlog("<%s> socket: %s", __func__, + strerror(errno)); + return (oifflags & ~IFF_UP); + } + + if_indextoname(ifindex, ifr.ifr_name); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + errorlog("<%s> ioctl:SIOCGIFFLAGS: failed for %s", + __func__, ifr.ifr_name); + close(s); + return (oifflags & ~IFF_UP); + } + close(s); + return (ifr.ifr_flags); +} + +#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) +int +lladdropt_length(struct sockaddr_dl *sdl) +{ + switch (sdl->sdl_type) { + case IFT_ETHER: + return(ROUNDUP8(ETHER_ADDR_LEN + 2)); + default: + return(0); + } +} + +void +lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) +{ + char *addr; + + ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ + + switch (sdl->sdl_type) { + case IFT_ETHER: + ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; + addr = (char *)(ndopt + 1); + memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); + break; + default: + errorlog("<%s> unsupported link type(%d)", + __func__, sdl->sdl_type); + exit(1); + } + + return; +} + +int +rtbuf_len() +{ + size_t len; + + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return(-1); + + return(len); +} + +#define FILTER_MATCH(type, filter) ((0x1 << type) & filter) +#define SIN6(s) ((struct sockaddr_in6 *)(s)) +#define SDL(s) ((struct sockaddr_dl *)(s)) +char * +get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) +{ + struct rt_msghdr *rtm; + struct ifa_msghdr *ifam; + struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX]; + + *lenp = 0; + for (rtm = (struct rt_msghdr *)buf; + rtm < (struct rt_msghdr *)lim; + rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) { + /* just for safety */ + if (!rtm->rtm_msglen) { + errorlog("<%s> rtm_msglen is 0 " + "(buf=%p lim=%p rtm=%p)", __func__, + buf, lim, rtm); + break; + } + if (FILTER_MATCH(rtm->rtm_type, filter) == 0) { + continue; + } + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + /* address related checks */ + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + if ((dst = rti_info[RTAX_DST]) == NULL || + dst->sa_family != AF_INET6) + continue; + + if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr)) + continue; + + if ((gw = rti_info[RTAX_GATEWAY]) == NULL || + gw->sa_family != AF_LINK) + continue; + if (ifindex && SDL(gw)->sdl_index != ifindex) + continue; + + if (rti_info[RTAX_NETMASK] == NULL) + continue; + + /* found */ + *lenp = rtm->rtm_msglen; + return (char *)rtm; + /* NOTREACHED */ + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + + /* address related checks */ + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + if ((ifa = rti_info[RTAX_IFA]) == NULL || + (ifa->sa_family != AF_INET && + ifa->sa_family != AF_INET6)) + continue; + + if (ifa->sa_family == AF_INET6 && + (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr))) + continue; + + if (ifindex && ifam->ifam_index != ifindex) + continue; + + /* found */ + *lenp = ifam->ifam_msglen; + return (char *)rtm; + /* NOTREACHED */ + case RTM_IFINFO: + /* found */ + *lenp = rtm->rtm_msglen; + return (char *)rtm; + /* NOTREACHED */ + } + } + + return (char *)rtm; +} +#undef FILTER_MATCH + +struct in6_addr * +get_addr(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + return(&SIN6(rti_info[RTAX_DST])->sin6_addr); +} + +int +get_rtm_ifindex(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); +} + +int +get_ifm_ifindex(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return ((int)ifm->ifm_index); +} + +int +get_ifam_ifindex(char *buf) +{ + struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf; + + return ((int)ifam->ifam_index); +} + +int +get_ifm_flags(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return (ifm->ifm_flags); +} + +int +get_prefixlen(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + u_char *p, *lim; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + sa = rti_info[RTAX_NETMASK]; + + p = (u_char *)(&SIN6(sa)->sin6_addr); + lim = (u_char *)sa + sa->sa_len; + return prefixlen(p, lim); +} + +int +prefixlen(u_char *p, u_char *lim) +{ + int masklen; + + for (masklen = 0; p < lim; p++) { + switch (*p) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + break; + case 0xfc: + masklen += 6; + break; + case 0xf8: + masklen += 5; + break; + case 0xf0: + masklen += 4; + break; + case 0xe0: + masklen += 3; + break; + case 0xc0: + masklen += 2; + break; + case 0x80: + masklen += 1; + break; + case 0x00: + break; + default: + return(-1); + } + } + + return(masklen); +} + +int +rtmsg_type(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + + return(rtm->rtm_type); +} + +int +rtmsg_len(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + + return(rtm->rtm_msglen); +} + +int +ifmsg_len(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return(ifm->ifm_msglen); +} + +/* + * alloc buffer and get if_msghdrs block from kernel, + * and put them into the buffer + */ +static void +get_iflist(char **buf, size_t *size) +{ + int mib[6]; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + + if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) { + errorlog("<%s> sysctl: iflist size get failed", + __func__); + exit(1); + } + if ((*buf = malloc(*size)) == NULL) { + errorlog("<%s> malloc failed", __func__); + exit(1); + } + if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) { + errorlog("<%s> sysctl: iflist get failed", + __func__); + exit(1); + } + return; +} + +struct if_msghdr * +get_interface_entry(int if_index) +{ + struct ifinfo *ifi = NULL; + struct if_msghdr *ifm = NULL; + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (if_index == ifi->ifm->ifm_index) { + ifm = ifi->ifm; + break; + } + } + return ifm; +} + +/* + * alloc buffer and parse if_msghdrs block passed as arg, + * and init the buffer as list of pointers ot each of the if_msghdr. + */ +static void +parse_iflist(char *buf, size_t bufsize) +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + char *lim; + + lim = buf + bufsize; + for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { + if (ifm->ifm_msglen == 0) { + errorlog("<%s> ifm_msglen is 0 " + "(buf=%p lim=%p ifm=%p)", __func__, + buf, lim, ifm); + return; + } + + if (ifm->ifm_type == RTM_IFINFO) { + struct ifinfo *ifi = NULL; + + if (get_interface_entry(ifm->ifm_index) != NULL) { + debuglog("Interface entry is already present for " + "interface index: %d. Skipping.", ifm->ifm_index); + continue; + } + + ELM_MALLOC(ifi, exit(1)); + ifi->ifm = ifm; + TAILQ_INSERT_TAIL(&ifilist, ifi, ifi_next); + } else { + errorlog("out of sync parsing NET_RT_IFLIST\n" + "expected %d, got %d\n msglen = %d\n" + "buf:%p, ifm:%p, lim:%p\n", + RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, + buf, ifm, lim); + exit (1); + } + for (ifam = (struct ifa_msghdr *) + ((char *)ifm + ifm->ifm_msglen); + ifam < (struct ifa_msghdr *)lim; + ifam = (struct ifa_msghdr *) + ((char *)ifam + ifam->ifam_msglen)) { + /* just for safety */ + if (!ifam->ifam_msglen) { + errorlog("<%s> ifa_msglen is 0 " + "(buf=%p lim=%p ifam=%p)", __func__, + buf, lim, ifam); + return; + } + if (ifam->ifam_type != RTM_NEWADDR) + break; + } + ifm = (struct if_msghdr *)ifam; + } +} + +static void +purge_iflist() +{ + struct ifinfo *ifi = NULL; + if (!TAILQ_EMPTY(&ifilist)) { + while ((ifi = TAILQ_FIRST(&ifilist)) != NULL) { + TAILQ_REMOVE(&ifilist, ifi, ifi_next); + free(ifi); + } + } +} + +void +init_iflist() +{ + purge_iflist(); + + if (ifblock) { + free(ifblock); + ifblock_size = 0; + } + + /* get iflist block from kernel */ + get_iflist(&ifblock, &ifblock_size); + + /* make list of pointers to each if_msghdr */ + parse_iflist(ifblock, ifblock_size); +} diff --git a/network_cmds/rtadvd.tproj/if.h b/network_cmds/rtadvd.tproj/if.h new file mode 100644 index 0000000..e733d63 --- /dev/null +++ b/network_cmds/rtadvd.tproj/if.h @@ -0,0 +1,64 @@ +/* $KAME: if.h,v 1.10 2003/02/24 11:29:10 ono Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define RTADV_TYPE2BITMASK(type) (0x1 << type) + +extern size_t ifblock_size; +extern char *ifblock; + +struct nd_opt_hdr; +struct sockaddr_dl *if_nametosdl(char *); +int if_getmtu(char *); +int if_getflags(int, int); +int lladdropt_length(struct sockaddr_dl *); +void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *); +int rtbuf_len(void); +char *get_next_msg(char *, char *, int, size_t *, int); +struct in6_addr *get_addr(char *); +int get_rtm_ifindex(char *); +int get_ifm_ifindex(char *); +int get_ifam_ifindex(char *); +int get_ifm_flags(char *); +int get_prefixlen(char *); +int prefixlen(u_char *, u_char *); +int rtmsg_type(char *); +int ifmsg_type(char *); +int rtmsg_len(char *); +int ifmsg_len(char *); +void init_iflist(void); + +struct ifinfo { + TAILQ_ENTRY(ifinfo) ifi_next; + struct if_msghdr * ifm; +}; + +struct if_msghdr *get_interface_entry(int if_index); + diff --git a/network_cmds/rtadvd.tproj/pathnames.h b/network_cmds/rtadvd.tproj/pathnames.h new file mode 100644 index 0000000..d288ab9 --- /dev/null +++ b/network_cmds/rtadvd.tproj/pathnames.h @@ -0,0 +1,4 @@ +/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */ +/* $FreeBSD: src/usr.sbin/rtadvd/pathnames.h,v 1.2.2.2 2001/07/03 11:02:14 ume Exp $ */ + +#define _PATH_RTADVDCONF "/etc/rtadvd.conf" diff --git a/network_cmds/rtadvd.tproj/rrenum.c b/network_cmds/rtadvd.tproj/rrenum.c new file mode 100644 index 0000000..ae04a55 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rrenum.c @@ -0,0 +1,484 @@ +/* $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/icmp6.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include "rtadvd.h" +#include "rrenum.h" +#include "if.h" + +#define RR_ISSET_SEGNUM(segnum_bits, segnum) \ + ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0) +#define RR_SET_SEGNUM(segnum_bits, segnum) \ + (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31))) + +struct rr_operation { + u_long rro_seqnum; + u_long rro_segnum_bits[8]; +}; + +static struct rr_operation rro; +static int rr_rcvifindex; +static int rrcmd2pco[RPM_PCO_MAX] = { + 0, + SIOCAIFPREFIX_IN6, + SIOCCIFPREFIX_IN6, + SIOCSGIFPREFIX_IN6 +}; +static int s = -1; + +/* + * Check validity of a Prefix Control Operation(PCO). + * Return 0 on success, 1 on failure. + */ +static int +rr_pco_check(int len, struct rr_pco_match *rpm) +{ + struct rr_pco_use *rpu, *rpulim; + int checklen; + + /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */ + if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ + (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ + errorlog("<%s> rpm_len %d is not 4N * 3", + __func__, rpm->rpm_len); + return 1; + } + /* rpm->rpm_code must be valid value */ + switch (rpm->rpm_code) { + case RPM_PCO_ADD: + case RPM_PCO_CHANGE: + case RPM_PCO_SETGLOBAL: + break; + default: + errorlog("<%s> unknown rpm_code %d", __func__, + rpm->rpm_code); + return 1; + } + /* rpm->rpm_matchlen must be 0 to 128 inclusive */ + if (rpm->rpm_matchlen > 128) { + errorlog("<%s> rpm_matchlen %d is over 128", + __func__, rpm->rpm_matchlen); + return 1; + } + + /* + * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be + * between 0 and 128 inclusive + */ + for (rpu = (struct rr_pco_use *)(rpm + 1), + rpulim = (struct rr_pco_use *)((char *)rpm + len); + rpu < rpulim; + rpu += 1) { + checklen = rpu->rpu_uselen; + checklen += rpu->rpu_keeplen; + /* + * omit these check, because either of rpu_uselen + * and rpu_keeplen is unsigned char + * (128 > rpu_uselen > 0) + * (128 > rpu_keeplen > 0) + * (rpu_uselen + rpu_keeplen > 0) + */ + if (checklen > 128) { + errorlog("<%s> sum of rpu_uselen %d and" + " rpu_keeplen %d is %d(over 128)", + __func__, rpu->rpu_uselen, + rpu->rpu_keeplen, + rpu->rpu_uselen + rpu->rpu_keeplen); + return 1; + } + } + return 0; +} + +static void +do_use_prefix(int len, struct rr_pco_match *rpm, + struct in6_rrenumreq *irr, int ifindex) +{ + struct rr_pco_use *rpu, *rpulim; + struct rainfo *rai; + struct prefix *pp; + + rpu = (struct rr_pco_use *)(rpm + 1); + rpulim = (struct rr_pco_use *)((char *)rpm + len); + + if (rpu == rpulim) { /* no use prefix */ + if (rpm->rpm_code == RPM_PCO_ADD) + return; + + irr->irr_u_uselen = 0; + irr->irr_u_keeplen = 0; + irr->irr_raf_mask_onlink = 0; + irr->irr_raf_mask_auto = 0; + irr->irr_vltime = 0; + irr->irr_pltime = 0; + memset(&irr->irr_flags, 0, sizeof(irr->irr_flags)); + irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */ + irr->irr_useprefix.sin6_family = 0; + irr->irr_useprefix.sin6_addr = in6addr_any; + if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && + errno != EADDRNOTAVAIL) + errorlog("<%s> ioctl: %s", __func__, + strerror(errno)); + return; + } + + for (rpu = (struct rr_pco_use *)(rpm + 1), + rpulim = (struct rr_pco_use *)((char *)rpm + len); + rpu < rpulim; + rpu += 1) { + /* init in6_rrenumreq fields */ + irr->irr_u_uselen = rpu->rpu_uselen; + irr->irr_u_keeplen = rpu->rpu_keeplen; + irr->irr_raf_mask_onlink = + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); + irr->irr_raf_mask_auto = + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); + irr->irr_vltime = ntohl(rpu->rpu_vltime); + irr->irr_pltime = ntohl(rpu->rpu_pltime); + irr->irr_raf_onlink = + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1; + irr->irr_raf_auto = + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1; + irr->irr_rrf_decrvalid = + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1; + irr->irr_rrf_decrprefd = + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1; + irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); + irr->irr_useprefix.sin6_family = AF_INET6; + irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; + + if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && + errno != EADDRNOTAVAIL) + errorlog("<%s> ioctl: %s", __func__, + strerror(errno)); + + /* very adhoc: should be rewritten */ + if (rpm->rpm_code == RPM_PCO_CHANGE && + IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) && + rpm->rpm_matchlen == rpu->rpu_uselen && + rpu->rpu_uselen == rpu->rpu_keeplen) { + if ((rai = if_indextorainfo(ifindex)) == NULL) + continue; /* non-advertising IF */ + + for (pp = rai->prefix.next; pp != &rai->prefix; + pp = pp->next) { + struct timeval now; + + if (prefix_match(&pp->prefix, pp->prefixlen, + &rpm->rpm_prefix, + rpm->rpm_matchlen)) { + /* change parameters */ + pp->validlifetime = ntohl(rpu->rpu_vltime); + pp->preflifetime = ntohl(rpu->rpu_pltime); + if (irr->irr_rrf_decrvalid) { + gettimeofday(&now, 0); + pp->vltimeexpire = + now.tv_sec + pp->validlifetime; + } else + pp->vltimeexpire = 0; + if (irr->irr_rrf_decrprefd) { + gettimeofday(&now, 0); + pp->pltimeexpire = + now.tv_sec + pp->preflifetime; + } else + pp->pltimeexpire = 0; + } + } + } + } +} + +/* + * process a Prefix Control Operation(PCO). + * return 0 on success, 1 on failure + */ +static int +do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) +{ + int ifindex = 0; + struct in6_rrenumreq irr; + + if ((rr_pco_check(len, rpm) != 0)) + return 1; + + if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + errorlog("<%s> socket: %s", __func__, + strerror(errno)); + exit(1); + } + + memset(&irr, 0, sizeof(irr)); + irr.irr_origin = PR_ORIG_RR; + irr.irr_m_len = rpm->rpm_matchlen; + irr.irr_m_minlen = rpm->rpm_minlen; + irr.irr_m_maxlen = rpm->rpm_maxlen; + irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix); + irr.irr_matchprefix.sin6_family = AF_INET6; + irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix; + + while (if_indextoname(++ifindex, irr.irr_name)) { + struct if_msghdr * ifm = get_interface_entry(ifindex); + if (ifm == NULL) { + debuglog("Couldn't find interface entry for %d. Skipping.", ifindex); + continue; + } + /* + * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off, + * the interface is not applied + */ + if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 && + (ifm->ifm_flags & IFF_UP) == 0) + continue; + /* TODO: interface scope check */ + do_use_prefix(len, rpm, &irr, ifindex); + } + if (errno == ENXIO) + return 0; + else if (errno) { + errorlog("<%s> if_indextoname: %s", __func__, + strerror(errno)); + return 1; + } + return 0; +} + +/* + * call do_pco() for each Prefix Control Operations(PCOs) in a received + * Router Renumbering Command packet. + * return 0 on success, 1 on failure + */ +static int +do_rr(int len, struct icmp6_router_renum *rr) +{ + struct rr_pco_match *rpm; + char *cp, *lim; + + lim = (char *)rr + len; + cp = (char *)(rr + 1); + len -= sizeof(struct icmp6_router_renum); + + /* get iflist block from kernel again, to get up-to-date information */ + init_iflist(); + + while (cp < lim) { + int rpmlen; + + rpm = (struct rr_pco_match *)cp; + if (len < sizeof(struct rr_pco_match)) { + tooshort: + errorlog("<%s> pkt too short. left len = %d. " + "gabage at end of pkt?", __func__, len); + return 1; + } + rpmlen = rpm->rpm_len << 3; + if (len < rpmlen) + goto tooshort; + + if (do_pco(rr, rpmlen, rpm)) { + errorlog("<%s> invalid PCO", __func__); + goto next; + } + + next: + cp += rpmlen; + len -= rpmlen; + } + + return 0; +} + +/* + * check validity of a router renumbering command packet + * return 0 on success, 1 on failure + */ +static int +rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, + struct in6_addr *dst) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + + /* omit rr minimal length check. hope kernel have done it. */ + /* rr_command length check */ + if (len < (sizeof(struct icmp6_router_renum) + + sizeof(struct rr_pco_match))) { + errorlog("<%s> rr_command len %d is too short", + __func__, len); + return 1; + } + + /* destination check. only for multicast. omit unicast check. */ + if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && + !IN6_IS_ADDR_MC_SITELOCAL(dst)) { + errorlog("<%s> dst mcast addr %s is illegal", + __func__, + inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN)); + return 1; + } + + /* seqnum and segnum check */ + if (rro.rro_seqnum > rr->rr_seqnum) { + errorlog("<%s> rcvd old seqnum %d from %s", + __func__, (u_int32_t)ntohl(rr->rr_seqnum), + inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN)); + return 1; + } + if (rro.rro_seqnum == rr->rr_seqnum && + (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 && + RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { + if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0) + errorlog("<%s> rcvd duped segnum %d from %s", + __func__, rr->rr_segnum, + inet_ntop(AF_INET6, from, ntopbuf, + INET6_ADDRSTRLEN)); + return 0; + } + + /* update seqnum */ + if (rro.rro_seqnum != rr->rr_seqnum) { + /* then must be "<" */ + + /* init rro_segnum_bits */ + memset(rro.rro_segnum_bits, 0, + sizeof(rro.rro_segnum_bits)); + } + rro.rro_seqnum = rr->rr_seqnum; + + return 0; +} + +static void +rr_command_input(int len, struct icmp6_router_renum *rr, + struct in6_addr *from, struct in6_addr *dst) +{ + /* rr_command validity check */ + if (rr_command_check(len, rr, from, dst)) + goto failed; + if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) == + ICMP6_RR_FLAGS_TEST) + return; + + /* do router renumbering */ + if (do_rr(len, rr)) { + goto failed; + } + + /* update segnum */ + RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); + + return; + + failed: + errorlog("<%s> received RR was invalid", __func__); + return; +} + +void +rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, + struct sockaddr_in6 *from, struct in6_addr *dst) +{ + char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + + debuglog("<%s> RR received from %s to %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* packet validation based on Section 4.1 of RFC2894 */ + if (len < sizeof(struct icmp6_router_renum)) { + noticelog("<%s>: RR short message (size %d) from %s to %s on %s", + __func__, len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * If the IPv6 destination address is neither an All Routers multicast + * address [AARCH] nor one of the receiving router's unicast addresses, + * the message MUST be discarded and SHOULD be logged to network + * management. + * We rely on the kernel input routine for unicast addresses, and thus + * check multicast destinations only. + */ + if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) { + noticelog("<%s>: RR message with invalid destination (%s) " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + rr_rcvifindex = pi->ipi6_ifindex; + + switch (rr->rr_code) { + case ICMP6_ROUTER_RENUMBERING_COMMAND: + rr_command_input(len, rr, &from->sin6_addr, dst); + /* TODO: send reply msg */ + break; + case ICMP6_ROUTER_RENUMBERING_RESULT: + /* RESULT will be processed by rrenumd */ + break; + case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET: + /* TODO: sequence number reset */ + break; + default: + errorlog("<%s> received unknown code %d", + __func__, rr->rr_code); + break; + + } + + return; +} diff --git a/network_cmds/rtadvd.tproj/rrenum.h b/network_cmds/rtadvd.tproj/rrenum.h new file mode 100644 index 0000000..b6ed486 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rrenum.h @@ -0,0 +1,33 @@ +/* $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *, + struct sockaddr_in6 *, struct in6_addr *); diff --git a/network_cmds/rtadvd.tproj/rtadvd.8 b/network_cmds/rtadvd.tproj/rtadvd.8 new file mode 100644 index 0000000..27429de --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd.8 @@ -0,0 +1,207 @@ +.\" $FreeBSD: src/usr.sbin/rtadvd/rtadvd.8,v 1.3.2.6 2001/08/16 15:56:30 ru Exp $ +.\" $KAME: rtadvd.8,v 1.24 2002/05/31 16:16:08 jinmei Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd August 27, 2011 +.Dt RTADVD 8 +.Os +.Sh NAME +.Nm rtadvd +.Nd router advertisement daemon +.Sh SYNOPSIS +.Nm +.Op Fl dDfMRs +.Op Fl c Ar configfile +.Op Fl F Ar dumpfile +.Op Fl p Ar pidfile +.Ar interface ... +.Sh DESCRIPTION +.Nm +sends router advertisement packets to the specified +.Ar interfaces . +.Pp +The program will daemonize itself on invocation. +It will then send router advertisement packets periodically, as well +as in response to router solicitation messages sent by end hosts. +.Pp +Router advertisements can be configured on a per-interface basis, as +described in +.Xr rtadvd.conf 5 . +.Pp +If there is no configuration file entry for an interface, +or if the configuration file does not exist altogether, +.Nm +sets all the parameters to their default values. +In particular, +.Nm +reads all the interface routes from the routing table and advertises +them as on-link prefixes. +.Pp +.Nm +also watches the routing table. +If an interface direct route is +added on an advertising interface and no static prefixes are +specified by the configuration file, +.Nm +adds the corresponding prefix to its advertising list. +.Pp +Similarly, when an interface direct route is deleted, +.Nm +will start advertising the prefixes with zero valid and preferred +lifetimes to help the receiving hosts switch to a new prefix when +renumbering. +Note, however, that the zero valid lifetime cannot invalidate the +autoconfigured addresses at a receiving host immediately. +According to the specification, the host will retain the address +for a certain period, which will typically be two hours. +The zero lifetimes rather intend to make the address deprecated, +indicating that a new non-deprecated address should be used as the +source address of a new connection. +This behavior will last for two hours. +Then +.Nm +will completely remove the prefix from the advertising list, +and succeeding advertisements will not contain the prefix information. +.Pp +Moreover, if the status of an advertising interface changes, +.Nm +will start or stop sending router advertisements according +to the latest status. +.Pp +The +.Fl s +option may be used to disable this behavior; +.Nm +will not watch the routing table and the whole functionality described +above will be suppressed. +.Pp +Basically, hosts MUST NOT send Router Advertisement messages at any +time (RFC 2461, Section 6.2.3). +However, it would sometimes be useful to allow hosts to advertise some +parameters such as prefix information and link MTU. +Thus, +.Nm +can be invoked if router lifetime is explicitly set zero on every +advertising interface. +.Pp +The command line options are: +.Bl -tag -width indent +.\" +.It Fl c +Specify an alternate location, +.Ar configfile , +for the configuration file. +By default, +.Pa /etc/rtadvd.conf +is used. +.It Fl d +Print debugging information. +.It Fl D +Even more debugging information is printed. +.It Fl f +This option is now deprecated and ignored. +.It Fl F +Specify an alternative file in which to dump internal states when +.Nm +receives signal +.Dv SIGUSR1 . +The default is +.Pa /var/run/rtadvd.dump . +.It Fl M +Specify an interface to join the all-routers site-local multicast group. +By default, +.Nm +tries to join the first advertising interface appearing on the command +line. +This option has meaning only with the +.Fl R +option, which enables routing renumbering protocol support. +.It Fl p +Specify an alternative file in which to store the process ID. +The default is +.Pa /var/run/rtadvd.pid. +.It Fl R +Accept router renumbering requests. +If you enable it, certain IPsec setup is suggested for security reasons. +This option is currently disabled, and is ignored by +.Nm +with a warning message. +.It Fl s +Do not add or delete prefixes dynamically. +Only statically configured prefixes, if any, will be advertised. +.El +.Pp +Upon receipt of signal +.Dv SIGUSR1 , +.Nm +will dump the current internal state into +.Pa /var/run/rtadvd.dump +or the file specified with option +.Fl F . +.Pp +Use +.Dv SIGTERM +to kill +.Nm +gracefully. +In this case, +.Nm +will transmit router advertisement with router lifetime 0 +to all the interfaces +.Pq in accordance with RFC2461 6.2.5 . +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/rtadvd.conf +The default configuration file. +.It Pa /var/run/rtadvd.pid +The default process ID file. +.It Pa /var/run/rtadvd.dump +The default file in which +.Nm +dumps its internal state. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr rtadvd.conf 5 , +.Xr rtsol 8 +.Sh HISTORY +The +.Nm +command first appeared in the WIDE Hydrangea IPv6 protocol stack kit. +.Sh BUGS +There used to be some text that recommended users not to let +.Nm +advertise Router Advertisement messages on an upstream link to avoid +undesirable +.Xr icmp6 4 +redirect messages. +However, based on the later discussion in the IETF ipng working group, +all routers should rather advertise the messages regardless of +the network topology, in order to ensure reachability. diff --git a/network_cmds/rtadvd.tproj/rtadvd.c b/network_cmds/rtadvd.tproj/rtadvd.c new file mode 100644 index 0000000..be5c0f8 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd.c @@ -0,0 +1,1650 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: rtadvd.c,v 1.82 2003/08/05 12:34:23 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet/icmp6.h> + +#include <arpa/inet.h> + +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <err.h> +#include <errno.h> +#include <libutil.h> +#include <string.h> +#include <stdlib.h> +#include "rtadvd.h" +#include "rrenum.h" +#include "advcap.h" +#include "timer.h" +#include "if.h" +#include "config.h" +#include "dump.h" + +struct msghdr rcvmhdr; +static u_char *rcvcmsgbuf; +static size_t rcvcmsgbuflen; +static u_char *sndcmsgbuf = NULL; +static size_t sndcmsgbuflen; +volatile sig_atomic_t do_dump; +volatile sig_atomic_t do_die; +struct msghdr sndmhdr; +struct iovec rcviov[2]; +struct iovec sndiov[2]; +struct sockaddr_in6 rcvfrom; +struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6}; +struct in6_addr in6a_site_allrouters; +static char *dumpfilename = "/var/run/rtadvd.dump"; +static char *pidfilename = "/var/run/rtadvd.pid"; +static struct pidfh *pfh; +static char *mcastif; +int sock; +int rtsock = -1; +int accept_rr = 0; +int dflag = 0, sflag = 0; +int so_traffic_class = SO_TC_CTL; /* use control class, by default */ +char *conffile = NULL; + +struct rainfo *ralist = NULL; + +struct nd_optlist { + struct nd_optlist *next; + struct nd_opt_hdr *opt; +}; +union nd_opts { + struct nd_opt_hdr *nd_opt_array[9]; + struct { + struct nd_opt_hdr *zero; + struct nd_opt_hdr *src_lladdr; + struct nd_opt_hdr *tgt_lladdr; + struct nd_opt_prefix_info *pi; + struct nd_opt_rd_hdr *rh; + struct nd_opt_mtu *mtu; + struct nd_optlist *list; + } nd_opt_each; +}; +#define nd_opts_src_lladdr nd_opt_each.src_lladdr +#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr +#define nd_opts_pi nd_opt_each.pi +#define nd_opts_rh nd_opt_each.rh +#define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_list nd_opt_each.list + +#define NDOPT_FLAG_SRCLINKADDR 0x1 +#define NDOPT_FLAG_TGTLINKADDR 0x2 +#define NDOPT_FLAG_PREFIXINFO 0x4 +#define NDOPT_FLAG_RDHDR 0x8 +#define NDOPT_FLAG_MTU 0x10 + +u_int32_t ndopt_flags[] = { + 0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR, + NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU, +}; + +int main(int, char *[]); +static void set_die(int); +static void die(void); +static void sock_open(void); +static void rtsock_open(void); +static void rtadvd_input(void); +static void rs_input(int, struct nd_router_solicit *, + struct in6_pktinfo *, struct sockaddr_in6 *); +static void ra_input(int, struct nd_router_advert *, + struct in6_pktinfo *, struct sockaddr_in6 *); +static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, + struct sockaddr_in6 *); +static int nd6_options(struct nd_opt_hdr *, int, + union nd_opts *, u_int32_t); +static void free_ndopts(union nd_opts *); +static void ra_output(struct rainfo *); +static void rtmsg_input(void); +static void rtadvd_set_dump_file(int); +static void set_short_delay(struct rainfo *); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + fd_set *fdsetp, *selectfdp; + int fdmasks; + int maxfd = 0; + struct timeval *timeout; + int i, ch; + int fflag = 0; + pid_t pid, otherpid; + + /* get command line options and arguments */ + while ((ch = getopt(argc, argv, "c:dDF:fMp:Rs")) != -1) { + switch (ch) { + case 'c': + conffile = optarg; + break; + case 'd': + dflag = 1; + break; + case 'D': + dflag = 2; + break; + case 'f': + fflag = 1; + break; + case 'M': + mcastif = optarg; + break; + case 'R': + fprintf(stderr, "rtadvd: " + "the -R option is currently ignored.\n"); + /* accept_rr = 1; */ + /* run anyway... */ + break; + case 's': + sflag = 1; + break; + case 'p': + pidfilename = optarg; + break; + case 'F': + dumpfilename = optarg; + break; + } + } + argc -= optind; + argv += optind; + if (argc == 0) { + fprintf(stderr, + "usage: rtadvd [-dDfMRs] [-c conffile] " + "[-F dumpfile] [-p pidfile] interfaces...\n"); + exit(1); + } + + /* timer initialization */ + rtadvd_timer_init(); + + /* random value initialization */ + srandom((u_long)time(NULL)); + + /* get iflist block from kernel */ + init_iflist(); + + while (argc--) + getconfig(*argv++); + + if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) { + fprintf(stderr, "fatal: inet_pton failed\n"); + exit(1); + } + + pfh = pidfile_open(pidfilename, 0600, &otherpid); + if (pfh == NULL) { + if (errno == EEXIST) + errx(1, "%s already running, pid: %d", + getprogname(), otherpid); + errorlog("<%s> failed to open the pid log file, run anyway.", + __func__); + } + + if (!fflag) + daemon(1, 0); + + sock_open(); + + /* record the current PID */ + pid = getpid(); + pidfile_write(pfh); + + maxfd = sock; + if (sflag == 0) { + rtsock_open(); + if (rtsock > sock) + maxfd = rtsock; + } else + rtsock = -1; + + fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); + if ((fdsetp = malloc(fdmasks)) == NULL) { + err(1, "malloc"); + /*NOTREACHED*/ + } + if ((selectfdp = malloc(fdmasks)) == NULL) { + err(1, "malloc"); + /*NOTREACHED*/ + } + memset(fdsetp, 0, fdmasks); + FD_SET(sock, fdsetp); + if (rtsock >= 0) + FD_SET(rtsock, fdsetp); + + signal(SIGTERM, set_die); + signal(SIGUSR1, rtadvd_set_dump_file); + + while (1) { + memcpy(selectfdp, fdsetp, fdmasks); /* reinitialize */ + + if (do_dump) { /* SIGUSR1 */ + do_dump = 0; + rtadvd_dump_file(dumpfilename); + } + + if (do_die) { + die(); + /*NOTREACHED*/ + } + + /* timer expiration check and reset the timer */ + timeout = rtadvd_check_timer(); + + if (timeout != NULL) { + debuglog("<%s> set timer to %ld:%ld. waiting for " + "inputs or timeout", __func__, + (long int)timeout->tv_sec, + (long int)timeout->tv_usec); + } else { + debuglog("<%s> there's no timer. waiting for inputs", + __func__); + } + + if ((i = select(maxfd + 1, selectfdp, NULL, NULL, + timeout)) < 0) { + /* EINTR would occur upon SIGUSR1 for status dump */ + if (errno != EINTR) + errorlog( "<%s> select: %s", + __func__, strerror(errno)); + continue; + } + if (i == 0) /* timeout */ + continue; + if (rtsock != -1 && FD_ISSET(rtsock, selectfdp)) + rtmsg_input(); + if (FD_ISSET(sock, selectfdp)) + rtadvd_input(); + } + exit(0); /* NOTREACHED */ +} + +static void +rtadvd_set_dump_file(sig) + int sig; +{ + do_dump = 1; +} + +static void +set_die(sig) + int sig; +{ + do_die = 1; +} + +static void +die() +{ + struct rainfo *ra; + int i; + const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; + + if (dflag > 1) { + debuglog("<%s> cease to be an advertising router\n", + __func__); + } + + for (ra = ralist; ra; ra = ra->next) { + ra->lifetime = 0; + make_packet(ra); + } + for (i = 0; i < retrans; i++) { + for (ra = ralist; ra; ra = ra->next) + ra_output(ra); + + if (retrans != 1) + sleep(MIN_DELAY_BETWEEN_RAS); + } + pidfile_remove(pfh); + exit(0); + /*NOTREACHED*/ +} + +static void +rtmsg_input() +{ + int n, type, ifindex = 0, plen; + size_t len; + char msg[2048], *next, *lim; + char ifname[IF_NAMESIZE]; + struct prefix *prefix; + struct rainfo *rai; + struct in6_addr *addr; + char addrbuf[INET6_ADDRSTRLEN]; + int prefixchange = 0; + + n = read(rtsock, msg, sizeof(msg)); + if (dflag > 1) { + debuglog( "<%s> received a routing message " + "(type = %d, len = %d)", __func__, rtmsg_type(msg), n); + } + if (n > rtmsg_len(msg)) { + /* + * This usually won't happen for messages received on + * a routing socket. + */ + if (dflag > 1) + debuglog("<%s> received data length is larger than " + "1st routing message len. multiple messages? " + "read %d bytes, but 1st msg len = %d", + __func__, n, rtmsg_len(msg)); +#if 0 + /* adjust length */ + n = rtmsg_len(msg); +#endif + } + + lim = msg + n; + for (next = msg; next < lim; next += len) { + struct if_msghdr * ifm = NULL; + int oldifflags; + + next = get_next_msg(next, lim, 0, &len, + RTADV_TYPE2BITMASK(RTM_ADD) | + RTADV_TYPE2BITMASK(RTM_DELETE) | + RTADV_TYPE2BITMASK(RTM_NEWADDR) | + RTADV_TYPE2BITMASK(RTM_DELADDR) | + RTADV_TYPE2BITMASK(RTM_IFINFO)); + if (len == 0) + break; + type = rtmsg_type(next); + switch (type) { + case RTM_ADD: + case RTM_DELETE: + ifindex = get_rtm_ifindex(next); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifindex = get_ifam_ifindex(next); + break; + case RTM_IFINFO: + ifindex = get_ifm_ifindex(next); + break; + default: + /* should not reach here */ + if (dflag > 1) { + debuglog("<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); + } + continue; + } + + if ((rai = if_indextorainfo(ifindex)) == NULL) { + if (dflag > 1) { + debuglog("<%s> route changed on " + "non advertising interface(%s)", + __func__, + if_indextoname(ifindex, ifname)); + } + continue; + } + ifm = get_interface_entry(ifindex); + if (ifm == NULL) { + debuglog("Couldn't find interface entry for %d. Skipping.", ifindex); + continue; + } + oldifflags = ifm->ifm_flags; + + switch (type) { + case RTM_ADD: + /* init ifflags because it may have changed */ + ifm->ifm_flags = + if_getflags(ifindex, ifm->ifm_flags); + + if (sflag) + break; /* we aren't interested in prefixes */ + + addr = get_addr(msg); + plen = get_prefixlen(msg); + /* sanity check for plen */ + /* as RFC2373, prefixlen is at least 4 */ + if (plen < 4 || plen > 127) { + infolog("<%s> new interface route's" + "plen %d is invalid for a prefix", + __func__, plen); + break; + } + prefix = find_prefix(rai, addr, plen); + if (prefix) { + if (prefix->timer) { + /* + * If the prefix has been invalidated, + * make it available again. + */ + update_prefix(prefix); + prefixchange = 1; + } else if (dflag > 1) { + debuglog("<%s> new prefix(%s/%d) " + "added on %s, " + "but it was already in list", + __func__, + inet_ntop(AF_INET6, addr, + (char *)addrbuf, INET6_ADDRSTRLEN), + plen, rai->ifname); + } + break; + } + make_prefix(rai, ifindex, addr, plen); + prefixchange = 1; + break; + case RTM_DELETE: + /* init ifflags because it may have changed */ + ifm->ifm_flags = if_getflags(ifindex, ifm->ifm_flags); + + if (sflag) + break; + + addr = get_addr(msg); + plen = get_prefixlen(msg); + /* sanity check for plen */ + /* as RFC2373, prefixlen is at least 4 */ + if (plen < 4 || plen > 127) { + infolog("<%s> deleted interface route's " + "plen %d is invalid for a prefix", + __func__, plen); + break; + } + prefix = find_prefix(rai, addr, plen); + if (prefix == NULL) { + if (dflag > 1) { + debuglog("<%s> prefix(%s/%d) was " + "deleted on %s, " + "but it was not in list", + __func__, + inet_ntop(AF_INET6, addr, + (char *)addrbuf, INET6_ADDRSTRLEN), + plen, rai->ifname); + } + break; + } + invalidate_prefix(prefix); + prefixchange = 1; + break; + case RTM_NEWADDR: + case RTM_DELADDR: + /* init ifflags because it may have changed */ + ifm->ifm_flags = if_getflags(ifindex, ifm->ifm_flags); + break; + case RTM_IFINFO: + ifm->ifm_flags = get_ifm_flags(next); + break; + default: + /* should not reach here */ + if (dflag > 1) { + debuglog("<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); + } + return; + } + + /* check if an interface flag is changed */ + if ((oldifflags & IFF_UP) && /* UP to DOWN */ + !(ifm->ifm_flags & IFF_UP)) { + infolog("<%s> interface %s becomes down. stop timer.", + __func__, rai->ifname); + rtadvd_remove_timer(&rai->timer); + } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */ + (ifm->ifm_flags & IFF_UP)) { + infolog("<%s> interface %s becomes up. restart timer.", + __func__, rai->ifname); + + rai->initcounter = 0; /* reset the counter */ + rai->waiting = 0; /* XXX */ + rai->timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, rai, rai); + ra_timer_update((void *)rai, &rai->timer->tm); + rtadvd_set_timer(&rai->timer->tm, rai->timer); + } else if (prefixchange && + (ifm->ifm_flags & IFF_UP)) { + /* + * An advertised prefix has been added or invalidated. + * Will notice the change in a short delay. + */ + rai->initcounter = 0; + set_short_delay(rai); + } + } + + return; +} + +void +rtadvd_input() +{ + int i; + int *hlimp = NULL; +#ifdef OLDRAWSOCKET + struct ip6_hdr *ip; +#endif + struct icmp6_hdr *icp; + int ifindex = 0; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + struct in6_addr dst = in6addr_any; + struct if_msghdr *ifm = NULL; + + /* + * Get message. We reset msg_controllen since the field could + * be modified if we had received a message before setting + * receive options. + */ + rcvmhdr.msg_controllen = rcvcmsgbuflen; + if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0) + return; + + /* extract optional information via Advanced API */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); + cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + ifindex = pi->ipi6_ifindex; + dst = pi->ipi6_addr; + } + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *)CMSG_DATA(cm); + } + if (ifindex == 0) { + errorlog("<%s> failed to get receiving interface", + __func__); + return; + } + if (hlimp == NULL) { + errorlog("<%s> failed to get receiving hop limit", + __func__); + return; + } + + ifm = get_interface_entry(pi->ipi6_ifindex); + /* + * If we happen to receive data on an interface which is now gone + * or down, just discard the data. + */ + if (ifm == NULL || + (ifm->ifm_flags & IFF_UP) == 0) { + infolog("<%s> received data on a disabled interface (%s)", + __func__, + (ifm == NULL) ? "[gone]" : + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + +#ifdef OLDRAWSOCKET + if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { + errorlog("<%s> packet size(%d) is too short", + __func__, i); + return; + } + + ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base; + icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */ +#else + if (i < sizeof(struct icmp6_hdr)) { + errorlog("<%s> packet size(%d) is too short", + __func__, i); + return; + } + + icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; +#endif + + switch (icp->icmp6_type) { + case ND_ROUTER_SOLICIT: + /* + * Message verification - RFC-2461 6.1.1 + * XXX: these checks must be done in the kernel as well, + * but we can't completely rely on them. + */ + if (*hlimp != 255) { + noticelog("<%s> RS with invalid hop limit(%d) " + "received from %s on %s", + __func__, *hlimp, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (icp->icmp6_code) { + noticelog("<%s> RS with invalid ICMP6 code(%d) " + "received from %s on %s", + __func__, icp->icmp6_code, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (i < sizeof(struct nd_router_solicit)) { + noticelog("<%s> RS from %s on %s does not have enough " + "length (len = %d)", + __func__, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); + return; + } + rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom); + break; + case ND_ROUTER_ADVERT: + /* + * Message verification - RFC-2461 6.1.2 + * XXX: there's a same dilemma as above... + */ + if (*hlimp != 255) { + noticelog("<%s> RA with invalid hop limit(%d) " + "received from %s on %s", + __func__, *hlimp, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (icp->icmp6_code) { + noticelog("<%s> RA with invalid ICMP6 code(%d) " + "received from %s on %s", + __func__, icp->icmp6_code, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (i < sizeof(struct nd_router_advert)) { + noticelog("<%s> RA from %s on %s does not have enough " + "length (len = %d)", + __func__, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); + return; + } + ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom); + break; + case ICMP6_ROUTER_RENUMBERING: + if (accept_rr == 0) { + errorlog("<%s> received a router renumbering " + "message, but not allowed to be accepted", + __func__); + break; + } + rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom, + &dst); + break; + default: + /* + * Note that this case is POSSIBLE, especially just + * after invocation of the daemon. This is because we + * could receive message after opening the socket and + * before setting ICMP6 type filter(see sock_open()). + */ + errorlog("<%s> invalid icmp type(%d)", + __func__, icp->icmp6_type); + return; + } + + return; +} + +static void +rs_input(int len, struct nd_router_solicit *rs, + struct in6_pktinfo *pi, struct sockaddr_in6 *from) +{ + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + union nd_opts ndopts; + struct rainfo *ra; + struct soliciter *sol; + + debuglog( + "<%s> RS received from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* ND option check */ + memset(&ndopts, 0, sizeof(ndopts)); + if (nd6_options((struct nd_opt_hdr *)(rs + 1), + len - sizeof(struct nd_router_solicit), + &ndopts, NDOPT_FLAG_SRCLINKADDR)) { + infolog("<%s> ND option check failed for an RS from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * If the IP source address is the unspecified address, there + * must be no source link-layer address option in the message. + * (RFC-2461 6.1.1) + */ + if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && + ndopts.nd_opts_src_lladdr) { + infolog("<%s> RS from unspecified src on %s has a link-layer" + " address option", + __func__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + + ra = ralist; + while (ra != NULL) { + if (pi->ipi6_ifindex == ra->ifindex) + break; + ra = ra->next; + } + if (ra == NULL) { + infolog("<%s> RS received on non advertising interface(%s)", + __func__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + + ra->rsinput++; /* increment statistics */ + + /* + * Decide whether to send RA according to the rate-limit + * consideration. + */ + + /* record sockaddr waiting for RA, if possible */ + sol = (struct soliciter *)malloc(sizeof(*sol)); + if (sol) { + sol->addr = *from; + /* XXX RFC2553 need clarification on flowinfo */ + sol->addr.sin6_flowinfo = 0; + sol->next = ra->soliciter; + ra->soliciter = sol; + } + + /* + * If there is already a waiting RS packet, don't + * update the timer. + */ + if (ra->waiting++) + goto done; + + set_short_delay(ra); + + done: + free_ndopts(&ndopts); + return; +} + +static void +set_short_delay(rai) + struct rainfo *rai; +{ + long delay; /* must not be greater than 1000000 */ + struct timeval interval, now, min_delay, tm_tmp, *rest; + + if (rai->timer == NULL) + return; + /* + * Compute a random delay. If the computed value + * corresponds to a time later than the time the next + * multicast RA is scheduled to be sent, ignore the random + * delay and send the advertisement at the + * already-scheduled time. RFC-2461 6.2.6 + */ +#ifdef HAVE_ARC4RANDOM + delay = arc4random_uniform(MAX_RA_DELAY_TIME); +#else + delay = random() % MAX_RA_DELAY_TIME; +#endif + interval.tv_sec = 0; + interval.tv_usec = delay; + rest = rtadvd_timer_rest(rai->timer); + if (TIMEVAL_LT(*rest, interval)) { + debuglog("<%s> random delay is larger than " + "the rest of the current timer", __func__); + interval = *rest; + } + + /* + * If we sent a multicast Router Advertisement within + * the last MIN_DELAY_BETWEEN_RAS seconds, schedule + * the advertisement to be sent at a time corresponding to + * MIN_DELAY_BETWEEN_RAS plus the random value after the + * previous advertisement was sent. + */ + gettimeofday(&now, NULL); + TIMEVAL_SUB(&now, &rai->lastsent, &tm_tmp); + min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; + min_delay.tv_usec = 0; + if (TIMEVAL_LT(tm_tmp, min_delay)) { + TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay); + TIMEVAL_ADD(&min_delay, &interval, &interval); + } + rtadvd_set_timer(&interval, rai->timer); +} + +static void +ra_input(int len, struct nd_router_advert *ra, + struct in6_pktinfo *pi, struct sockaddr_in6 *from) +{ + struct rainfo *rai; + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + union nd_opts ndopts; + char *on_off[] = {"OFF", "ON"}; + u_int32_t reachabletime, retranstimer, mtu; + int inconsistent = 0; + + debuglog("<%s> RA received from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* ND option check */ + memset(&ndopts, 0, sizeof(ndopts)); + if (nd6_options((struct nd_opt_hdr *)(ra + 1), + len - sizeof(struct nd_router_advert), + &ndopts, NDOPT_FLAG_SRCLINKADDR | + NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) { + infolog("<%s> ND option check failed for an RA from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * RA consistency check according to RFC-2461 6.2.7 + */ + if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) { + infolog("<%s> received RA from %s on non-advertising" + " interface(%s)", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + rai->rainput++; /* increment statistics */ + + /* Cur Hop Limit value */ + if (ra->nd_ra_curhoplimit && rai->hoplimit && + ra->nd_ra_curhoplimit != rai->hoplimit) { + infolog("<%s> CurHopLimit inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, + ra->nd_ra_curhoplimit, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->hoplimit); + inconsistent++; + } + /* M flag */ + if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != + rai->managedflg) { + infolog("<%s> M flag inconsistent on %s:" + " %s from %s, %s from us", + __func__, + rai->ifname, + on_off[!rai->managedflg], + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + on_off[rai->managedflg]); + inconsistent++; + } + /* O flag */ + if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != + rai->otherflg) { + infolog("<%s> O flag inconsistent on %s:" + " %s from %s, %s from us", + __func__, + rai->ifname, + on_off[!rai->otherflg], + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + on_off[rai->otherflg]); + inconsistent++; + } + /* Reachable Time */ + reachabletime = ntohl(ra->nd_ra_reachable); + if (reachabletime && rai->reachabletime && + reachabletime != rai->reachabletime) { + infolog("<%s> ReachableTime inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, + reachabletime, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->reachabletime); + inconsistent++; + } + /* Retrans Timer */ + retranstimer = ntohl(ra->nd_ra_retransmit); + if (retranstimer && rai->retranstimer && + retranstimer != rai->retranstimer) { + infolog("<%s> RetranceTimer inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, + retranstimer, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->retranstimer); + inconsistent++; + } + /* Values in the MTU options */ + if (ndopts.nd_opts_mtu) { + mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); + if (mtu && rai->linkmtu && mtu != rai->linkmtu) { + infolog("<%s> MTU option value inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, mtu, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->linkmtu); + inconsistent++; + } + } + /* Preferred and Valid Lifetimes for prefixes */ + { + struct nd_optlist *optp = ndopts.nd_opts_list; + + if (ndopts.nd_opts_pi) { + if (prefix_check(ndopts.nd_opts_pi, rai, from)) + inconsistent++; + } + while (optp) { + if (prefix_check((struct nd_opt_prefix_info *)optp->opt, + rai, from)) + inconsistent++; + optp = optp->next; + } + } + + if (inconsistent) + rai->rainconsistent++; + + done: + free_ndopts(&ndopts); + return; +} + +/* return a non-zero value if the received prefix is inconsitent with ours */ +static int +prefix_check(struct nd_opt_prefix_info *pinfo, + struct rainfo *rai, struct sockaddr_in6 *from) +{ + u_int32_t preferred_time, valid_time; + struct prefix *pp; + int inconsistent = 0; + char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; + struct timeval now; + +#if 0 /* impossible */ + if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) + return(0); +#endif + + /* + * log if the adveritsed prefix has link-local scope(sanity check?) + */ + if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) { + infolog("<%s> link-local prefix %s/%d is advertised " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->ifname); + } + + if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix, + pinfo->nd_opt_pi_prefix_len)) == NULL) { + infolog("<%s> prefix %s/%d from %s on %s is not in our list", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->ifname); + return(0); + } + + preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); + if (pp->pltimeexpire) { + /* + * The lifetime is decremented in real time, so we should + * compare the expiration time. + * (RFC 2461 Section 6.2.7.) + * XXX: can we really expect that all routers on the link + * have synchronized clocks? + */ + gettimeofday(&now, NULL); + preferred_time += now.tv_sec; + + if (!pp->timer && rai->clockskew && + preferred_time - pp->pltimeexpire > rai->clockskew) { + infolog("<%s> preferred lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->pltimeexpire); + inconsistent++; + } + } else if (!pp->timer && preferred_time != pp->preflifetime) { + infolog("<%s> preferred lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->preflifetime); + } + + valid_time = ntohl(pinfo->nd_opt_pi_valid_time); + if (pp->vltimeexpire) { + gettimeofday(&now, NULL); + valid_time += now.tv_sec; + + if (!pp->timer && rai->clockskew && + valid_time - pp->vltimeexpire > rai->clockskew) { + infolog("<%s> valid lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->vltimeexpire); + inconsistent++; + } + } else if (!pp->timer && valid_time != pp->validlifetime) { + infolog("<%s> valid lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, valid_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->validlifetime); + inconsistent++; + } + + return(inconsistent); +} + +struct prefix * +find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) +{ + struct prefix *pp; + int bytelen, bitlen; + u_char bitmask; + + for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) { + if (plen != pp->prefixlen) + continue; + bytelen = plen / 8; + bitlen = plen % 8; + bitmask = 0xff << (8 - bitlen); + if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen)) + continue; + if (bitlen == 0 || + ((prefix->s6_addr[bytelen] & bitmask) == + (pp->prefix.s6_addr[bytelen] & bitmask))) { + return(pp); + } + } + + return(NULL); +} + +/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */ +int +prefix_match(struct in6_addr *p0, int plen0, + struct in6_addr *p1, int plen1) +{ + int bytelen, bitlen; + u_char bitmask; + + if (plen0 < plen1) + return(0); + bytelen = plen1 / 8; + bitlen = plen1 % 8; + bitmask = 0xff << (8 - bitlen); + if (memcmp((void *)p0, (void *)p1, bytelen)) + return(0); + if (bitlen == 0 || + ((p0->s6_addr[bytelen] & bitmask) == + (p1->s6_addr[bytelen] & bitmask))) { + return(1); + } + + return(0); +} + +static int +nd6_options(struct nd_opt_hdr *hdr, int limit, + union nd_opts *ndopts, u_int32_t optflags) +{ + int optlen = 0; + + for (; limit > 0; limit -= optlen) { + if (limit < sizeof(struct nd_opt_hdr)) { + infolog("<%s> short option header", __func__); + goto bad; + } + + hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen); + if (hdr->nd_opt_len == 0) { + infolog("<%s> bad ND option length(0) (type = %d)", + __func__, hdr->nd_opt_type); + goto bad; + } + optlen = hdr->nd_opt_len << 3; + if (optlen > limit) { + infolog("<%s> short option", __func__); + goto bad; + } + + if (hdr->nd_opt_type > ND_OPT_MTU) { + infolog("<%s> unknown ND option(type %d)", + __func__, hdr->nd_opt_type); + continue; + } + + if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) { + infolog("<%s> unexpected ND option(type %d)", + __func__, hdr->nd_opt_type); + continue; + } + + /* + * Option length check. Do it here for all fixed-length + * options. + */ + if ((hdr->nd_opt_type == ND_OPT_MTU && + (optlen != sizeof(struct nd_opt_mtu))) || + ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION && + optlen != sizeof(struct nd_opt_prefix_info)))) { + infolog("<%s> invalid option length", + __func__); + continue; + } + + switch (hdr->nd_opt_type) { + case ND_OPT_TARGET_LINKADDR: + case ND_OPT_REDIRECTED_HEADER: + break; /* we don't care about these options */ + case ND_OPT_SOURCE_LINKADDR: + case ND_OPT_MTU: + if (ndopts->nd_opt_array[hdr->nd_opt_type]) { + infolog("<%s> duplicated ND option (type = %d)", + __func__, hdr->nd_opt_type); + } + ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; + break; + case ND_OPT_PREFIX_INFORMATION: + { + struct nd_optlist *pfxlist; + + if (ndopts->nd_opts_pi == 0) { + ndopts->nd_opts_pi = + (struct nd_opt_prefix_info *)hdr; + continue; + } + if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) { + errorlog("<%s> can't allocate memory", + __func__); + goto bad; + } + pfxlist->next = ndopts->nd_opts_list; + pfxlist->opt = hdr; + ndopts->nd_opts_list = pfxlist; + + break; + } + default: /* impossible */ + break; + } + } + + return(0); + + bad: + free_ndopts(ndopts); + + return(-1); +} + +static void +free_ndopts(union nd_opts *ndopts) +{ + struct nd_optlist *opt = ndopts->nd_opts_list, *next; + + while (opt) { + next = opt->next; + free(opt); + opt = next; + } +} + +void +sock_open() +{ + struct icmp6_filter filt; + struct ipv6_mreq mreq; + struct rainfo *ra = ralist; + int on; + /* XXX: should be max MTU attached to the node */ + static u_char answer[1500]; + + rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + rcvcmsgbuf = (u_char *)malloc(rcvcmsgbuflen); + if (rcvcmsgbuf == NULL) { + errorlog("<%s> not enough core", __func__); + exit(1); + } + + sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen); + if (sndcmsgbuf == NULL) { + errorlog("<%s> not enough core", __func__); + exit(1); + } + + if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + errorlog("<%s> socket: %s", __func__, + strerror(errno)); + exit(1); + } + + (void) setsockopt(sock, SOL_SOCKET, SO_TRAFFIC_CLASS, + (void *)&so_traffic_class, sizeof (so_traffic_class)); + + /* specify to tell receiving interface */ + on = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) { + errorlog("<%s> IPV6_RECVPKTINFO: %s", + __func__, strerror(errno)); + exit(1); + } +#else /* old adv. API */ + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) { + errorlog("<%s> IPV6_PKTINFO: %s", + __func__, strerror(errno)); + exit(1); + } +#endif + + on = 1; + /* specify to tell value of hoplimit field of received IP6 hdr */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) { + errorlog( "<%s> IPV6_RECVHOPLIMIT: %s", + __func__, strerror(errno)); + exit(1); + } +#else /* old adv. API */ + if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) { + errorlog("<%s> IPV6_HOPLIMIT: %s", + __func__, strerror(errno)); + exit(1); + } +#endif + + on = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_DONTFRAG, &on, + sizeof(on)) < 0) { + errorlog("<%s> IPV6_DONTFRAG: %s", + __func__, strerror(errno)); + exit(1); + } + + ICMP6_FILTER_SETBLOCKALL(&filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); + if (accept_rr) + ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); + if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) { + errorlog("<%s> IICMP6_FILTER: %s", + __func__, strerror(errno)); + exit(1); + } + + /* + * join all routers multicast address on each advertising interface. + */ + if (inet_pton(AF_INET6, ALLROUTERS_LINK, + &mreq.ipv6mr_multiaddr.s6_addr) + != 1) { + errorlog("<%s> inet_pton failed(library bug?)", + __func__); + exit(1); + } + while (ra) { + mreq.ipv6mr_interface = ra->ifindex; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) < 0) { + errorlog("<%s> IPV6_JOIN_GROUP(link) on %s: %s", + __func__, ra->ifname, strerror(errno)); + exit(1); + } + ra = ra->next; + } + + /* + * When attending router renumbering, join all-routers site-local + * multicast group. + */ + if (accept_rr) { + if (inet_pton(AF_INET6, ALLROUTERS_SITE, + &in6a_site_allrouters) != 1) { + errorlog("<%s> inet_pton failed(library bug?)", + __func__); + exit(1); + } + mreq.ipv6mr_multiaddr = in6a_site_allrouters; + if (mcastif) { + if ((mreq.ipv6mr_interface = if_nametoindex(mcastif)) + == 0) { + errorlog("<%s> invalid interface: %s", + __func__, mcastif); + exit(1); + } + } else + mreq.ipv6mr_interface = ralist->ifindex; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + errorlog("<%s> IPV6_JOIN_GROUP(site) on %s: %s", + __func__, + mcastif ? mcastif : ralist->ifname, + strerror(errno)); + exit(1); + } + } + + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = (caddr_t)answer; + rcviov[0].iov_len = sizeof(answer); + rcvmhdr.msg_name = (caddr_t)&rcvfrom; + rcvmhdr.msg_namelen = sizeof(rcvfrom); + rcvmhdr.msg_iov = rcviov; + rcvmhdr.msg_iovlen = 1; + rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; + rcvmhdr.msg_controllen = rcvcmsgbuflen; + + /* initialize msghdr for sending packets */ + sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); + sndmhdr.msg_iov = sndiov; + sndmhdr.msg_iovlen = 1; + sndmhdr.msg_control = (caddr_t)sndcmsgbuf; + sndmhdr.msg_controllen = sndcmsgbuflen; + + return; +} + +/* open a routing socket to watch the routing table */ +static void +rtsock_open() +{ + if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + errorlog("<%s> socket: %s", __func__, strerror(errno)); + exit(1); + } +} + +struct rainfo * +if_indextorainfo(int idx) +{ + struct rainfo *rai = ralist; + + for (rai = ralist; rai; rai = rai->next) { + if (rai->ifindex == idx) + return(rai); + } + + return(NULL); /* search failed */ +} + +static void +ra_output(rainfo) +struct rainfo *rainfo; +{ + int i; + struct cmsghdr *cm; + struct in6_pktinfo *pi; + struct soliciter *sol, *nextsol; + struct if_msghdr *ifm = get_interface_entry(rainfo->ifindex); + + if (ifm == NULL || + (ifm->ifm_flags & IFF_UP) == 0) { + debuglog("<%s> %s is not up, skip sending RA", + __func__, rainfo->ifname); + return; + } + + make_packet(rainfo); /* XXX: inefficient */ + + sndmhdr.msg_name = (caddr_t)&sin6_allnodes; + sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data; + sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen; + + cm = CMSG_FIRSTHDR(&sndmhdr); + /* specify the outgoing interface */ + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pi = (struct in6_pktinfo *)CMSG_DATA(cm); + memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ + pi->ipi6_ifindex = rainfo->ifindex; + + /* specify the hop limit of the packet */ + { + int hoplimit = 255; + + cm = CMSG_NXTHDR(&sndmhdr, cm); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_HOPLIMIT; + cm->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); + } + + debuglog("<%s> send RA on %s, # of waitings = %d", + __func__, rainfo->ifname, rainfo->waiting); + + i = sendmsg(sock, &sndmhdr, 0); + + if (i < 0 || i != rainfo->ra_datalen) { + if (i < 0) { + errorlog("<%s> sendmsg on %s: %s", + __func__, rainfo->ifname, + strerror(errno)); + } + } + /* update counter */ + if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) + rainfo->initcounter++; + rainfo->raoutput++; + + /* + * unicast advertisements + * XXX commented out. reason: though spec does not forbit it, unicast + * advert does not really help + */ + for (sol = rainfo->soliciter; sol; sol = nextsol) { + nextsol = sol->next; + + sol->next = NULL; + free(sol); + } + rainfo->soliciter = NULL; + + /* update timestamp */ + gettimeofday(&rainfo->lastsent, NULL); + + /* reset waiting conter */ + rainfo->waiting = 0; +} + +/* process RA timer */ +struct rtadvd_timer * +ra_timeout(void *data) +{ + struct rainfo *rai = (struct rainfo *)data; + +#ifdef notyet + /* if necessary, reconstruct the packet. */ +#endif + + debuglog("<%s> RA timer on %s is expired", + __func__, rai->ifname); + + ra_output(rai); + + return(rai->timer); +} + +/* update RA timer */ +void +ra_timer_update(void *data, struct timeval *tm) +{ + struct rainfo *rai = (struct rainfo *)data; + long interval; + + /* + * Whenever a multicast advertisement is sent from an interface, + * the timer is reset to a uniformly-distributed random value + * between the interface's configured MinRtrAdvInterval and + * MaxRtrAdvInterval (RFC2461 6.2.4). + */ + interval = rai->mininterval; + interval += random() % (rai->maxinterval - rai->mininterval); + + /* + * The first advertisement is sent as soon as rtadvd starts up + * and for the next few advertisements (up to + * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval + * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer + * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. + * (RFC-2461 6.2.4) + */ + if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && + interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) + interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; + + tm->tv_sec = rai->initcounter == 0 ? 0 : interval; + tm->tv_usec = 0; + + debuglog("<%s> RA timer on %s is set to %ld:%ld", + __func__, rai->ifname, + (long int)tm->tv_sec, (long int)tm->tv_usec); + + return; +} diff --git a/network_cmds/rtadvd.tproj/rtadvd.conf b/network_cmds/rtadvd.tproj/rtadvd.conf new file mode 100644 index 0000000..6d109b2 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd.conf @@ -0,0 +1,20 @@ +# $KAME: rtadvd.conf,v 1.13 2003/06/25 03:45:21 itojun Exp $ +# +# Note: All of the following parameters have default values defined +# in specifications, and hence you usually do not have to set them +# by hand unless you need special non-default values. +# +# You even do not need to create the configuration file. rtadvd +# would usually work well without a configuration file. +# See also: rtadvd(8) + +# per-interface definitions. +# Mainly IPv6 prefixes are configured in this part. However, rtadvd +# automatically learns appropriate prefixes from the kernel's routing +# table, and advertises the prefixes, so you don't have to configure +# this part, either. +# If you don't want the automatic advertisement, (uncomment and) configure +# this part by hand, and then invoke rtadvd with the -s option. + +#ef0:\ +# :addr="3ffe:501:ffff:1000::":prefixlen#64: diff --git a/network_cmds/rtadvd.tproj/rtadvd.conf.5 b/network_cmds/rtadvd.tproj/rtadvd.conf.5 new file mode 100644 index 0000000..87978fc --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd.conf.5 @@ -0,0 +1,509 @@ +.\" $KAME: rtadvd.conf.5,v 1.50 2005/01/14 05:30:59 jinmei Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd February 24, 2012 +.Dt RTADVD.CONF 5 +.Os +.Sh NAME +.Nm rtadvd.conf +.Nd config file for router advertisement daemon +.Sh DESCRIPTION +This file describes how the router advertisement packets must be constructed +for each of the interfaces. +.Pp +As described in +.Xr rtadvd 8 , +you do not have to set this configuration file up at all, +unless you need some special configurations. +You may even omit the file as a whole. +In such cases, the +.Nm rtadvd +daemon will automatically configure itself using default values +specified in the specification. +.Pp +It obeys the famous +.Xr termcap 5 +file format. +Each line in the file describes a network interface. +Fields are separated by a colon +.Pq Sq \&: , +and each field contains one capability description. +Lines may be concatenated by the +.Sq \e +character. +The comment marker is the +.Sq \&# +character. +.Sh CAPABILITIES +Capabilities describe the value to be filled into ICMPv6 router +advertisement messages and to control +.Xr rtadvd 8 +behavior. +Therefore, you are encouraged to read IETF neighbor discovery documents +if you would like to modify the sample configuration file. +.Pp +Note that almost all items have default values. +If you omit an item, the default value of the item will be used. +.Pp +There are two items which control the interval of sending router advertisements. +These items can be omitted, then +.Nm rtadvd +will use the default values. +.Bl -tag -width indent +.It Cm \&maxinterval +(num) The maximum time allowed between sending unsolicited +multicast router advertisements +.Pq unit: seconds . +The default value is 600. +Its value must be no less than 4 seconds +and no greater than 1800 seconds. +.It Cm \&mininterval +(num) The minimum time allowed between sending unsolicited multicast +router advertisements +.Pq unit: seconds . +The default value is the one third of value of +.Cm maxinterval . +Its value must be no less than 3 seconds and no greater than .75 * +the value of +.Cm maxinterval . +.El +.Pp +The following items are for ICMPv6 router advertisement message +header. +These items can be omitted, then +.Nm rtadvd +will use the default values. +.Bl -tag -width indent +.It Cm \&chlim +(num) The value for Cur Hop Limit field. +The default value is 64. +.It Cm \&raflags +(str or num) A 8-bit flags field in router advertisement message header. +This field can be specified either as a case-sensitive string or as an +integer. +A string consists of characters each of which corresponds to a +particular flag bit(s). +An integer should be the logical OR of all enabled bits. +Bit 7 +.Po +.Li 'm' or 0x80 +.Pc +means Managed address configuration flag bit, +and Bit 6 +.Po +.Li 'o' or 0x40 +.Pc +means Other stateful configuration flag bit. +Bit 4 +.Po +.Li 0x10 +.Pc +and Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode router preference. +Bits 01 +.Po +or 'h' +.Pc +means high, 00 means medium, and 11 +.Po +or 'l' +.Pc +means low. +Bits 10 is reserved, and must not be specified. +There is no character to specify the medium preference explicitly. +The default value of the entire flag is 0 +.Po +or a null string, +.Pc +which means no additional +configuration methods, and the medium router preference. +.It Cm \&rltime +(num) Router lifetime field +.Pq unit: seconds . +The value must be either zero or between +the value of +.Cm maxinterval +and 9000. +When +.Nm rtadvd +runs on a host, this value must explicitly set 0 on all the +advertising interfaces as described in +.Xr rtadvd 8 . +The default value is 1800. +.It Cm \&rtime +(num) Reachable time field +.Pq unit: milliseconds . +The default value is 0, which means unspecified by this router. +.It Cm \&retrans +(num) Retrans Timer field +.Pq unit: milliseconds . +The default value is 0, which means unspecified by this router. +.El +.Pp +The following items are for ICMPv6 prefix information option, +which will be attached to router advertisement header. +These items can be omitted, then +.Nm rtadvd +will automatically get appropriate prefixes from the kernel's routing table, +and advertise the prefixes with the default parameters. +Keywords other than +.Cm clockskew +can be augmented with a number, like +.Dq Li prefix2 , +to specify multiple prefixes. +.Bl -tag -width indent +.It Cm \&clockskew +(num) Time skew to adjust link propagation delays and clock skews +between routers on the link +.Pq unit: seconds . +This value is used in consistency check for locally-configured and +advertised prefix lifetimes, and has its meaning when the local router +configures a prefix on the link with a lifetime that decrements in +real time. +If the value is 0, it means the consistency check will be skipped +for such prefixes. +The default value is 0. +.It Cm \&prefixlen +(num) Prefix length field. +The default value is 64. +.It Cm \&pinfoflags +(str or num) A 8-bit flags field in prefix information option. +This field can be specified either as a case-sensitive string or as an +integer. +A string consists of characters each of which corresponds to a +particular flag bit(s). +An integer should be the logical OR of all enabled bits. +Bit 7 +.Po +.Li 'l' or 0x80 +.Pc +means On-link flag bit, +and Bit 6 +.Po +.Li 'a' or 0x40 +.Pc +means Autonomous address-configuration flag bit. +The default value is "la" or 0xc0, i.e., both bits are set. +.It Cm \&addr +(str) The address filled into Prefix field. +Since +.Dq \&: +is used for +.Xr termcap 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +.It Cm \&vltime +(num) Valid lifetime field +.Pq unit: seconds . +The default value is 2592000 (30 days). +.It Cm \&vltimedecr +(bool) This item means the advertised valid lifetime will decrement +in real time, which is disabled by default. +.It Cm \&pltime +(num) Preferred lifetime field +.Pq unit: seconds . +The default value is 604800 (7 days). +.It Cm \&pltimedecr +(bool) This item means the advertised preferred lifetime will decrement +in real time, which is disabled by default. +.El +.Pp +The following item is for ICMPv6 MTU option, +which will be attached to router advertisement header. +This item can be omitted, then +.Nm rtadvd +will use the default value. +.Bl -tag -width indent +.It Cm \&mtu +(num or str) MTU (maximum transmission unit) field. +If 0 is specified, it means that the option will not be included. +The default value is 0. +If the special string +.Dq auto +is specified for this item, MTU option will be included and its value +will be set to the interface MTU automatically. +.El +.Pp +The following item controls ICMPv6 source link-layer address option, +which will be attached to router advertisement header. +As noted above, you can just omit the item, then +.Nm rtadvd +will use the default value. +.Bl -tag -width indent +.It Cm \&nolladdr +(bool) By default +.Po +if +.Cm \&nolladdr +is not specified +.Pc , +.Xr rtadvd 8 +will try to get link-layer address for the interface from the kernel, +and attach that in source link-layer address option. +If this capability exists, +.Xr rtadvd 8 +will not attach source link-layer address option to +router advertisement packets. +.El +.Pp +The following item controls ICMPv6 home agent information option, +which was defined with mobile IPv6 support. +It will be attached to router advertisement header just like other options do. +.Bl -tag -width indent +.It Cm \&hapref +(num) Specifies home agent preference. +If set to non-zero, +.Cm \&hatime +must be present as well. +.It Cm \&hatime +(num) Specifies home agent lifetime. +.El +.Pp +When mobile IPv6 support is turned on for +.Xr rtadvd 8 , +advertisement interval option will be attached to router advertisement +packet, by configuring +.Cm \&maxinterval +explicitly. +.Pp +The following items are for ICMPv6 route information option, +which will be attached to router advertisement header. +These items are optional. +Each items can be augmented with number, like +.Dq Li rtplen2 , +to specify multiple routes. +.Bl -tag -width indent +.It Cm \&rtprefix +(str) The prefix filled into the Prefix field of route information option. +Since +.Dq \&: +is used for +.Xr termcap 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +.It Cm \&rtplen +(num) Prefix length field in route information option. +The default value is 64. +.It Cm \&rtflags +(str or num) A 8-bit flags field in route information option. +Currently only the preference values are defined. +The notation is same as that of the raflags field. +Bit 4 +.Po +.Li 0x10 +.Pc +and +Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode the route preference for the route. +The default value is 0x00, i.e., medium preference. +.It Cm \&rtltime +(num) route lifetime field in route information option. +.Pq unit: seconds . +Since the specification does not define the default value of this +item, the value for this item should be specified by hand. +However, +.Nm rtadvd +allows this item to be unspecified, and uses the router lifetime +as the default value in such a case, just for compatibility with an +old version of the program. +.El +.Pp +In the above list, each keyword beginning with +.Dq Li rt +could be replaced with the one beginning with +.Dq Li rtr +for backward compatibility reason. +For example, +.Cm rtrplen +is accepted instead of +.Cm rtplen . +However, keywords that start with +.Dq Li rtr +have basically been obsoleted, and should not be used any more. +.Pp +You can also refer one line from another by using +.Cm tc +capability. +See +.Xr termcap 5 +for details on the capability. +.Pp +The following items are for the ICMPv6 recursive DNS server (RDNSS) option, +which will be attached to the router advertisement header. +.Bl -tag -width indent +.It Cm \&rdnssaddrs +(num) Number of recursive DNS server addresses. +Its default is 0, so it must explicitly be set to positive values +if you want to specify any DNS server address. +If its value is 0, no DNS server information is sent. +If its value is more than 1, you must specify the index of the address +for the +.Cm rdnssaddr +item below. +Indices vary from 0 to N-1, where N is the +value of +.Cm rdnssaddrs . +Each index shall follow the name of +.Cm rdnssaddr , +e.g., +.Dq rdnssaddr0 . +.It Cm \&rdnssaddr +(str) The IPv6 address of the recursive DNS server. +Since +.Dq \&: +is used for +.Xr termcap 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +This field cannot be +omitted if the value of +.Cm rdnssaddrs +is more than 0. +.It Cm \&rdnsslifetime +(num) The lifetime field in RDNSS option. +(unit: seconds). +The default value is 2 * the value of +.Cm \&maxinterval , +which is also the maximum value that should be set. The minimum value is +.Cm \&maxinterval . +If you specify a value outside of this range, a message is logged. +.El +.Pp +The following items are for the ICMPv6 DNS search list (DNSSL) option, +which will be attached to the router advertisement header. +.Bl -tag -width indent +.It Cm \&dnssldomains +(num) Number of DNS search domains. +Its default is 0, so it must explicitly be set to positive values +if you want to specify any DNS search domains. +If its value is 0, no DNS search domain information is sent. +If its value is more than 1, you must specify the index of the search domain +for the +.Cm dnssldomain +item below. +Indices vary from 0 to N-1, where N is the +value of +.Cm dnssldomains . +Each index shall follow the name of +.Cm dnssldomain , +e.g., +.Dq dnssldomain0 . +.It Cm \&dnssldomain +(str) The DNS search domain. +This field cannot be +omitted if the value of +.Cm dnssldomains +is more than 0. +.It Cm \&dnssllifetime +(num) The lifetime field in the DNSSL option. +(unit: seconds). +The default value is 2 * the value of +.Cm \&maxinterval , +which is also the maximum value that should be set. The minimum value is +.Cm \&maxinterval . +If you specify a value outside of this range, a message is logged. +.El +.Sh EXAMPLES +As presented above, all of the advertised parameters have default values +defined in specifications, and hence you usually do not have to set them +by hand, unless you need special non-default values. +It can cause interoperability problem if you use an ill-configured +parameter. +.Pp +To override a configuration parameter, you can specify the parameter alone. +With the following configuration, +.Xr rtadvd 8 +overrides the router lifetime parameter for the +.Li ne0 +interface. +.Bd -literal -offset +ne0:\\ + :rltime#0: +.Ed +.Pp +The following example manually configures prefixes advertised from the +.Li ef0 +interface. +The configuration must be used with the +.Fl s +option to +.Xr rtadvd 8 . +.Bd -literal -offset +ef0:\\ + :addr="3ffe:501:ffff:1000::":prefixlen#64: +.Ed +.Pp +The following example presents the default values in an explicit manner. +The configuration is provided just for reference purposes; +YOU DO NOT NEED TO HAVE IT AT ALL. +.Bd -literal -offset +default:\\ + :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\ + :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0: +ef0:\\ + :addr="3ffe:501:ffff:1000::":prefixlen#64:tc=default: +.Ed +.Sh SEE ALSO +.Xr termcap 5 , +.Xr rtadvd 8 , +.Xr rtsol 8 +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 2461 +.Re +.Rs +.%A Richard Draves +.%T Default Router Preferences and More-Specific Routes +.%R draft-ietf-ipngwg-router-selection-xx.txt +.Re +.Rs +.%A J. Jeong +.%A S. Park +.%A L. Beloeil +.%A S. Madanapalli +.%T IPv6 Router Advertisement Option for DNS Configuration +.%R RFC 5006 +.Re +.Sh HISTORY +The +.Xr rtadvd 8 +and the configuration file +.Nm +first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" .Sh BUGS +.\" (to be written) diff --git a/network_cmds/rtadvd.tproj/rtadvd.h b/network_cmds/rtadvd.tproj/rtadvd.h new file mode 100644 index 0000000..504f398 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd.h @@ -0,0 +1,205 @@ +/* $KAME: rtadvd.h,v 1.26 2003/08/05 12:34:23 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "rtadvd_logging.h" + +#define ELM_MALLOC(p, error_action) \ + do { \ + p = malloc(sizeof(*p)); \ + if (p == NULL) { \ + errorlog("<%s> malloc failed: %s", \ + __func__, strerror(errno)); \ + error_action; \ + } \ + memset(p, 0, sizeof(*p)); \ + } while(0) + +#define ROUTEINFO 1 + +#define ALLNODES "ff02::1" +#define ALLROUTERS_LINK "ff02::2" +#define ALLROUTERS_SITE "ff05::2" +#define ANY "::" +#define RTSOLLEN 8 + +/* protocol constants and default values */ +#define DEF_MAXRTRADVINTERVAL 600 +#define DEF_ADVLINKMTU 0 +#define DEF_ADVREACHABLETIME 0 +#define DEF_ADVRETRANSTIMER 0 +#define DEF_ADVCURHOPLIMIT 64 +#define DEF_ADVVALIDLIFETIME 2592000 +#define DEF_ADVPREFERREDLIFETIME 604800 + +#define MAXROUTERLIFETIME 9000 +#define MIN_MAXINTERVAL 4 +#define MAX_MAXINTERVAL 1800 +#define MIN_MININTERVAL 3 +#define MAXREACHABLETIME 3600000 + +#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 +#define MAX_INITIAL_RTR_ADVERTISEMENTS 3 +#define MAX_FINAL_RTR_ADVERTISEMENTS 1 +#define MIN_DELAY_BETWEEN_RAS 3 +#define MAX_RA_DELAY_TIME 500000 /* usec */ + +#define PREFIX_FROM_KERNEL 1 +#define PREFIX_FROM_CONFIG 2 +#define PREFIX_FROM_DYNAMIC 3 + +struct prefix { + struct prefix *next; /* forward link */ + struct prefix *prev; /* previous link */ + + struct rainfo *rainfo; /* back pointer to the interface */ + + struct rtadvd_timer *timer; /* expiration timer. used when a prefix + * derived from the kernel is deleted. + */ + + u_int32_t validlifetime; /* AdvValidLifetime */ + long vltimeexpire; /* expiration of vltime; decrement case only */ + u_int32_t preflifetime; /* AdvPreferredLifetime */ + long pltimeexpire; /* expiration of pltime; decrement case only */ + u_int onlinkflg; /* bool: AdvOnLinkFlag */ + u_int autoconfflg; /* bool: AdvAutonomousFlag */ + int prefixlen; + int origin; /* from kernel or config */ + struct in6_addr prefix; +}; + +#ifdef ROUTEINFO +struct rtinfo { + struct rtinfo *prev; /* previous link */ + struct rtinfo *next; /* forward link */ + + u_int32_t ltime; /* route lifetime */ + u_int rtpref; /* route preference */ + int prefixlen; + struct in6_addr prefix; +}; +#endif + +struct soliciter { + struct soliciter *next; + struct sockaddr_in6 addr; +}; + +struct rdnss { + struct rdnss *next; /* forward link */ + struct rdnss *prev; /* previous link */ + + struct in6_addr addr; +}; + +struct dnssl { + struct dnssl *next; + struct dnssl *prev; + + char domain[1]; +}; + +struct rainfo { + /* pointer for list */ + struct rainfo *next; + + /* timer related parameters */ + struct rtadvd_timer *timer; + int initcounter; /* counter for the first few advertisements */ + struct timeval lastsent; /* timestamp when the latest RA was sent */ + int waiting; /* number of RS waiting for RA */ + + /* interface information */ + int ifindex; + int advlinkopt; /* bool: whether include link-layer addr opt */ + struct sockaddr_dl *sdl; + char ifname[16]; + int phymtu; /* mtu of the physical interface */ + + /* Router configuration variables */ + u_short lifetime; /* AdvDefaultLifetime */ + u_int maxinterval; /* MaxRtrAdvInterval */ + u_int mininterval; /* MinRtrAdvInterval */ + int managedflg; /* AdvManagedFlag */ + int otherflg; /* AdvOtherConfigFlag */ + + int rtpref; /* router preference */ + u_int32_t linkmtu; /* AdvLinkMTU */ + u_int32_t reachabletime; /* AdvReachableTime */ + u_int32_t retranstimer; /* AdvRetransTimer */ + u_int hoplimit; /* AdvCurHopLimit */ + struct prefix prefix; /* AdvPrefixList(link head) */ + int pfxs; /* number of prefixes */ + long clockskew; /* used for consisitency check of lifetimes */ + +#ifdef ROUTEINFO + struct rtinfo route; /* route information option (link head) */ + int routes; /* number of route information options */ +#endif + + /* Recursive DNS Servers RFC5006 */ + struct rdnss rdnss_list; + int rdnss_length; + u_int32_t rdnss_lifetime; + + /* DNS Search List RFC6106 */ + struct dnssl dnssl_list; + int dnssl_length; + u_int32_t dnssl_lifetime; + u_int32_t dnssl_option_length; + + /* Captive Portal RFC 7710 */ + char * capport; + u_int32_t capport_length; + u_int32_t capport_option_length; + + /* actual RA packet data and its length */ + size_t ra_datalen; + u_char *ra_data; + + /* statistics */ + u_quad_t raoutput; /* number of RAs sent */ + u_quad_t rainput; /* number of RAs received */ + u_quad_t rainconsistent; /* number of RAs inconsistent with ours */ + u_quad_t rsinput; /* number of RSs received */ + + /* info about soliciter */ + struct soliciter *soliciter; /* recent solication source */ +}; + +struct rtadvd_timer *ra_timeout(void *); +void ra_timer_update(void *, struct timeval *); + +int prefix_match(struct in6_addr *, int, struct in6_addr *, int); +struct rainfo *if_indextorainfo(int); +struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int); + +extern struct in6_addr in6a_site_allrouters; diff --git a/network_cmds/rtadvd.tproj/rtadvd_logging.c b/network_cmds/rtadvd.tproj/rtadvd_logging.c new file mode 100644 index 0000000..90dec08 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd_logging.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Apple Inc. All rights reserved. + * + * This document is the property of Apple Inc. + * It is considered confidential and proprietary. + * + * This document may not be reproduced or transmitted in any form, + * in whole or in part, without the express written permission of + * Apple Inc. + */ +#include <assert.h> +#include <os/log_private.h> + +#define kRtadvdLoggerID "com.apple.rtadvd" +static os_log_t rtadvdLogger = NULL; /* Handle for Logger */ + +static boolean_t rtadvd_logger_create(void); + +static boolean_t +rtadvd_logger_create(void) +{ + assert(rtadvdLogger == NULL); + rtadvdLogger = os_log_create(kRtadvdLoggerID, "daemon"); + + if (rtadvdLogger == NULL) { + os_log_error(OS_LOG_DEFAULT, "Couldn't create os log object"); + } + + return (rtadvdLogger != NULL); +} + +void +rtadvdLog(int level, const char *format, ...) +{ + va_list args; + + if (rtadvdLogger == NULL && !rtadvd_logger_create()) { + return; + } + + va_start(args, format); + os_log_with_args(rtadvdLogger, level, format, args, __builtin_return_address(0)); + va_end(args); + return; +} diff --git a/network_cmds/rtadvd.tproj/rtadvd_logging.h b/network_cmds/rtadvd.tproj/rtadvd_logging.h new file mode 100644 index 0000000..11273e6 --- /dev/null +++ b/network_cmds/rtadvd.tproj/rtadvd_logging.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 Apple Inc. All rights reserved. + * + * This document is the property of Apple Inc. + * It is considered confidential and proprietary. + * + * This document may not be reproduced or transmitted in any form, + * in whole or in part, without the express written permission of + * Apple Inc. + */ +#include <os/log.h> +extern void rtadvdLog(int level, const char *format, ...); + +#define errorlog(__format, ...) \ +rtadvdLog(OS_LOG_TYPE_DEFAULT, __format, ## __VA_ARGS__) + +#define noticelog(__format, ...) \ +rtadvdLog(OS_LOG_TYPE_DEFAULT, __format, ## __VA_ARGS__) + +#define infolog(__format, ...) \ +rtadvdLog(OS_LOG_TYPE_INFO, __format, ## __VA_ARGS__) + +#define debuglog(__format, ...) \ +rtadvdLog(OS_LOG_TYPE_DEBUG, __format, ## __VA_ARGS__) diff --git a/network_cmds/rtadvd.tproj/timer.c b/network_cmds/rtadvd.tproj/timer.c new file mode 100644 index 0000000..3134fc0 --- /dev/null +++ b/network_cmds/rtadvd.tproj/timer.c @@ -0,0 +1,205 @@ +/* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/time.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <search.h> +#include "timer.h" +#include "rtadvd_logging.h" + +static struct rtadvd_timer timer_head; + +#define MILLION 1000000 +#define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\ + (t1)->tv_usec == (t2)->tv_usec) + +static struct timeval tm_max = {0x7fffffff, 0x7fffffff}; + +void +rtadvd_timer_init() +{ + memset(&timer_head, 0, sizeof(timer_head)); + + timer_head.next = timer_head.prev = &timer_head; + timer_head.tm = tm_max; +} + +struct rtadvd_timer * +rtadvd_add_timer(struct rtadvd_timer *(*timeout)(void *), + void (*update)(void *, struct timeval *), + void *timeodata, void *updatedata) +{ + struct rtadvd_timer *newtimer; + + if ((newtimer = malloc(sizeof(*newtimer))) == NULL) { + errorlog("<%s> can't allocate memory", __func__); + exit(1); + } + + memset(newtimer, 0, sizeof(*newtimer)); + + if (timeout == NULL) { + errorlog("<%s> timeout function unspecified", __func__); + exit(1); + } + newtimer->expire = timeout; + newtimer->update = update; + newtimer->expire_data = timeodata; + newtimer->update_data = updatedata; + newtimer->tm = tm_max; + + /* link into chain */ + insque(newtimer, &timer_head); + + return(newtimer); +} + +void +rtadvd_remove_timer(struct rtadvd_timer **timer) +{ + remque(*timer); + free(*timer); + *timer = NULL; +} + +void +rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer) +{ + struct timeval now; + + /* reset the timer */ + gettimeofday(&now, NULL); + + TIMEVAL_ADD(&now, tm, &timer->tm); + + /* update the next expiration time */ + if (TIMEVAL_LT(timer->tm, timer_head.tm)) + timer_head.tm = timer->tm; + + return; +} + +/* + * Check expiration for each timer. If a timer expires, + * call the expire function for the timer and update the timer. + * Return the next interval for select() call. + */ +struct timeval * +rtadvd_check_timer() +{ + static struct timeval returnval; + struct timeval now; + struct rtadvd_timer *tm = timer_head.next, *tm_next; + + gettimeofday(&now, NULL); + + timer_head.tm = tm_max; + + for (tm = timer_head.next; tm != &timer_head; tm = tm_next) { + tm_next = tm->next; + + if (TIMEVAL_LEQ(tm->tm, now)) { + if (((*tm->expire)(tm->expire_data) == NULL)) + continue; /* the timer was removed */ + if (tm->update) + (*tm->update)(tm->update_data, &tm->tm); + TIMEVAL_ADD(&tm->tm, &now, &tm->tm); + } + + if (TIMEVAL_LT(tm->tm, timer_head.tm)) + timer_head.tm = tm->tm; + } + + if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) { + /* no need to timeout */ + return(NULL); + } else if (TIMEVAL_LT(timer_head.tm, now)) { + /* this may occur when the interval is too small */ + returnval.tv_sec = returnval.tv_usec = 0; + } else + TIMEVAL_SUB(&timer_head.tm, &now, &returnval); + return(&returnval); +} + +struct timeval * +rtadvd_timer_rest(struct rtadvd_timer *timer) +{ + static struct timeval returnval, now; + + gettimeofday(&now, NULL); + if (TIMEVAL_LEQ(timer->tm, now)) { + debuglog("<%s> a timer must be expired, but not yet", + __func__); + returnval.tv_sec = returnval.tv_usec = 0; + } + else + TIMEVAL_SUB(&timer->tm, &now, &returnval); + + return(&returnval); +} + +/* result = a + b */ +void +TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec + b->tv_usec) < MILLION) { + result->tv_usec = l; + result->tv_sec = a->tv_sec + b->tv_sec; + } + else { + result->tv_usec = l - MILLION; + result->tv_sec = a->tv_sec + b->tv_sec + 1; + } +} + +/* + * result = a - b + * XXX: this function assumes that a >= b. + */ +void +TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec - b->tv_usec) >= 0) { + result->tv_usec = l; + result->tv_sec = a->tv_sec - b->tv_sec; + } + else { + result->tv_usec = MILLION + l; + result->tv_sec = a->tv_sec - b->tv_sec - 1; + } +} diff --git a/network_cmds/rtadvd.tproj/timer.h b/network_cmds/rtadvd.tproj/timer.h new file mode 100644 index 0000000..78314b7 --- /dev/null +++ b/network_cmds/rtadvd.tproj/timer.h @@ -0,0 +1,64 @@ +/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* a < b */ +#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) && \ + ((a).tv_usec < (b).tv_usec))) + +/* a <= b */ +#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) &&\ + ((a).tv_usec <= (b).tv_usec))) + +struct rtadvd_timer { + struct rtadvd_timer *next; + struct rtadvd_timer *prev; + struct rainfo *rai; + struct timeval tm; + + struct rtadvd_timer *(*expire)(void *); /* expiration function */ + void *expire_data; + void (*update)(void *, struct timeval *); /* update function */ + void *update_data; +}; + +void rtadvd_timer_init(void); +struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *), + void (*)(void *, struct timeval *), void *, void *); +void rtadvd_set_timer(struct timeval *, struct rtadvd_timer *); +void rtadvd_remove_timer(struct rtadvd_timer **); +struct timeval * rtadvd_check_timer(void); +struct timeval * rtadvd_timer_rest(struct rtadvd_timer *); +void TIMEVAL_ADD(struct timeval *, struct timeval *, + struct timeval *); +void TIMEVAL_SUB(struct timeval *, struct timeval *, + struct timeval *); diff --git a/network_cmds/rtsol.tproj/dump.c b/network_cmds/rtsol.tproj/dump.c new file mode 100644 index 0000000..69937f0 --- /dev/null +++ b/network_cmds/rtsol.tproj/dump.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: dump.c,v 1.8 2000/10/05 22:20:39 itojun Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/rtsold/dump.c,v 1.1.2.3 2001/07/03 11:02:16 ume Exp $ + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> + +#include <syslog.h> +#include <time.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "rtsold.h" + +static FILE *fp; + +extern struct ifinfo *iflist; + +static void dump_interface_status __P((void)); +static char *sec2str __P((time_t)); +char *ifstatstr[] = { + "IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE", "OPTIMISTIC" +}; + +static void +dump_interface_status() +{ + struct ifinfo *ifinfo; + struct timeval now; + + gettimeofday(&now, NULL); + + for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { + fprintf(fp, "Interface %s\n", ifinfo->ifname); + fprintf(fp, " probe interval: "); + if (ifinfo->probeinterval) { + fprintf(fp, "%d\n", ifinfo->probeinterval); + fprintf(fp, " probe timer: %d\n", ifinfo->probetimer); + } + else { + fprintf(fp, "infinity\n"); + fprintf(fp, " no probe timer\n"); + } + fprintf(fp, " interface status: %s\n", + ifinfo->active > 0 ? "active" : "inactive"); + fprintf(fp, " rtsold status: %s\n", ifstatstr[ifinfo->state]); + fprintf(fp, " carrier detection: %s\n", + ifinfo->mediareqok ? "available" : "unavailable"); + fprintf(fp, " probes: %d, dadcount = %d\n", + ifinfo->probes, ifinfo->dadcount); + if (ifinfo->timer.tv_sec == tm_max.tv_sec && + ifinfo->timer.tv_usec == tm_max.tv_usec) + fprintf(fp, " no timer\n"); + else { + fprintf(fp, " timer: interval=%d:%d, expire=%s\n", + (int)ifinfo->timer.tv_sec, + (int)ifinfo->timer.tv_usec, + (ifinfo->expire.tv_sec < now.tv_sec) ? "expired" + : sec2str(ifinfo->expire.tv_sec - now.tv_sec)); + } + fprintf(fp, " number of valid RAs: %d\n", ifinfo->racnt); + } +} + +void +rtsold_dump_file(dumpfile) + char *dumpfile; +{ + if ((fp = fopen(dumpfile, "w")) == NULL) { + warnmsg(LOG_WARNING, __FUNCTION__, "open a dump file(%s): %s", + dumpfile, strerror(errno)); + return; + } + + dump_interface_status(); + + fclose(fp); +} + +static char * +sec2str(total) + time_t total; +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + + return(result); +} diff --git a/network_cmds/rtsol.tproj/if.c b/network_cmds/rtsol.tproj/if.c new file mode 100644 index 0000000..b46c781 --- /dev/null +++ b/network_cmds/rtsol.tproj/if.c @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: if.c,v 1.15 2001/05/22 06:04:17 jinmei Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/rtsold/if.c,v 1.2.2.3 2001/07/03 11:02:16 ume Exp $ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/queue.h> + +#include <net/if.h> +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) +#include <net/if_var.h> +#endif /* __FreeBSD__ >= 3 */ +#include <net/if_types.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#if defined ( __FreeBSD__) || defined (__APPLE__) +# include <net/ethernet.h> +#endif +#ifdef __NetBSD__ +#include <net/if_ether.h> +#endif +#if defined(__bsdi__) || defined(__OpenBSD__) +# include <netinet/in.h> +# include <netinet/if_ether.h> +#endif +#include <netinet/in.h> +#include <netinet/icmp6.h> + +#include <netinet6/in6_var.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#ifdef HAVE_GETIFADDRS +#include <ifaddrs.h> +#endif + +#include "rtsold.h" + +extern int rssock; +static int ifsock; + +static int get_llflag __P((const char *name)); +#ifndef HAVE_GETIFADDRS +static unsigned int if_maxindex __P((void)); +#endif +static void get_rtaddrs __P((int addrs, struct sockaddr *sa, + struct sockaddr **rti_info)); + +int +ifinit() +{ + ifsock = rssock; + + return(0); +} + +int +interface_up(char *name) +{ + struct ifreq ifr; + int llflag; + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s", + strerror(errno)); + return(-1); + } + if (!(ifr.ifr_flags & IFF_UP)) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, + "ioctl(SIOCSIFFLAGS): %s", strerror(errno)); + } + return(-1); + } + + warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name); + + llflag = get_llflag(name); + if (llflag < 0) { + warnmsg(LOG_WARNING, __FUNCTION__, + "get_llflag() failed, anyway I'll try"); + return 0; + } + + if (!(llflag & (IN6_IFF_NOTREADY | IN6_IFF_DADPROGRESS))) { + warnmsg(LOG_DEBUG, __FUNCTION__, + "%s is ready", name); + return(0); + } else { + if (llflag & IN6_IFF_TENTATIVE) { + warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative", + name); + return IFS_TENTATIVE; + } + if (llflag & IN6_IFF_OPTIMISTIC) { + warnmsg(LOG_DEBUG, __FUNCTION__, "%s is optimistic", + name); + return IFS_OPTIMISTIC; + } + if (llflag & IN6_IFF_DUPLICATED) + warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated", + name); + return -1; + } +} + +int +interface_status(struct ifinfo *ifinfo) +{ + char *ifname = ifinfo->ifname; + struct ifreq ifr; + struct ifmediareq ifmr; + + /* get interface flags */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s", + ifname, strerror(errno)); + return(-1); + } + /* + * if one of UP and RUNNING flags is dropped, + * the interface is not active. + */ + if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { + goto inactive; + } + + /* Next, check carrier on the interface, if possible */ + if (!ifinfo->mediareqok) + goto active; + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + if (errno != EINVAL) { + warnmsg(LOG_DEBUG, __FUNCTION__, + "ioctl(SIOCGIFMEDIA) on %s: %s", + ifname, strerror(errno)); + return(-1); + } + /* + * EINVAL simply means that the interface does not support + * the SIOCGIFMEDIA ioctl. We regard it alive. + */ + ifinfo->mediareqok = 0; + goto active; + } + + if (ifmr.ifm_status & IFM_AVALID) { + switch(ifmr.ifm_active & IFM_NMASK) { + case IFM_ETHER: + if (ifmr.ifm_status & IFM_ACTIVE) + goto active; + else + goto inactive; + break; + default: + goto inactive; + } + } + + inactive: + return(0); + + active: + return(1); +} + +#define ROUNDUP(a, size) \ + (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) + +#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ + ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ + sizeof(uint32_t)) :\ + sizeof(uint32_t))) +#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) + +int +lladdropt_length(struct sockaddr_dl *sdl) +{ + switch(sdl->sdl_type) { + case IFT_ETHER: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif + return(ROUNDUP8(ETHER_ADDR_LEN + 2)); + default: + return(0); + } +} + +void +lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) +{ + char *addr; + + ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ + + switch(sdl->sdl_type) { + case IFT_ETHER: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif + ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; + addr = (char *)(ndopt + 1); + memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); + break; + default: + warnmsg(LOG_ERR, __FUNCTION__, + "unsupported link type(%d)", sdl->sdl_type); + exit(1); + } + + return; +} + +struct sockaddr_dl * +if_nametosdl(char *name) +{ + int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + char *buf, *next, *lim; + size_t len; + struct if_msghdr *ifm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl = NULL, *ret_sdl; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return(NULL); + if ((buf = malloc(len)) == NULL) + return(NULL); + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + free(buf); + return(NULL); + } + + lim = buf + len; + for (next = buf; next < lim; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_IFINFO) { + sa = (struct sockaddr *)(ifm + 1); + get_rtaddrs(ifm->ifm_addrs, sa, rti_info); + if ((sa = rti_info[RTAX_IFP]) != NULL) { + if (sa->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)sa; + if (strlen(name) != sdl->sdl_nlen) + continue; /* not same len */ + if (strncmp(&sdl->sdl_data[0], + name, + sdl->sdl_nlen) == 0) { + break; + } + } + } + } + } + if (next == lim) { + /* search failed */ + free(buf); + return(NULL); + } + + if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) + return(NULL); + memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); + + free(buf); + return(ret_sdl); +} + +int +getinet6sysctl(int code) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; + int value; + size_t size; + + mib[3] = code; + size = sizeof(value); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0) + return -1; + else + return value; +} + +/*------------------------------------------------------------*/ + +/* get ia6_flags for link-local addr on if. returns -1 on error. */ +static int +get_llflag(const char *name) +{ +#ifdef HAVE_GETIFADDRS + struct ifaddrs *ifap, *ifa; + struct in6_ifreq ifr6; + struct sockaddr_in6 *sin6; + int s; + + if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s", + strerror(errno)); + exit(1); + } + if (getifaddrs(&ifap) != 0) { + warnmsg(LOG_ERR, __FUNCTION__, "etifaddrs: %s", + strerror(errno)); + exit(1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (strlen(ifa->ifa_name) != strlen(name) + || strncmp(ifa->ifa_name, name, strlen(name)) != 0) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + continue; + + memset(&ifr6, 0, sizeof(ifr6)); + strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); + memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len); + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, + "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno)); + exit(1); + } + + freeifaddrs(ifap); + close(s); + return ifr6.ifr_ifru.ifru_flags6; + } + + freeifaddrs(ifap); + close(s); + return -1; +#else + int s; + unsigned int maxif; + struct ifreq *iflist; + struct ifconf ifconf; + struct ifreq *ifr, *ifr_end; + struct sockaddr_in6 *sin6; + struct in6_ifreq ifr6; + + maxif = if_maxindex() + 1; + iflist = (struct ifreq *)malloc(maxif * BUFSIZ); /* XXX */ + if (iflist == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, "not enough core"); + exit(1); + } + + if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s", + strerror(errno)); + exit(1); + } + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.ifc_req = iflist; + ifconf.ifc_len = maxif * BUFSIZ; /* XXX */ + if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFCONF): %s", + strerror(errno)); + exit(1); + } + + /* Look for this interface in the list */ + ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len); + for (ifr = ifconf.ifc_req; + ifr < ifr_end; + ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + + ifr->ifr_addr.sa_len)) { + if (strlen(ifr->ifr_name) != strlen(name) + || strncmp(ifr->ifr_name, name, strlen(name)) != 0) + continue; + if (ifr->ifr_addr.sa_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)&ifr->ifr_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + continue; + + memset(&ifr6, 0, sizeof(ifr6)); + strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); + memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len); + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, + "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno)); + exit(1); + } + + free(iflist); + close(s); + return ifr6.ifr_ifru.ifru_flags6; + } + + free(iflist); + close(s); + return -1; +#endif +} + +#ifndef HAVE_GETIFADDRS +static unsigned int +if_maxindex() +{ + struct if_nameindex *p, *p0; + unsigned int max = 0; + + p0 = if_nameindex(); + for (p = p0; p && p->if_index && p->if_name; p++) { + if (max < p->if_index) + max = p->if_index; + } + if_freenameindex(p0); + return max; +} +#endif + +static void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + NEXT_SA(sa); + } + else + rti_info[i] = NULL; + } +} diff --git a/network_cmds/rtsol.tproj/probe.c b/network_cmds/rtsol.tproj/probe.c new file mode 100644 index 0000000..305d0a2 --- /dev/null +++ b/network_cmds/rtsol.tproj/probe.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $KAME: probe.c,v 1.10 2000/08/13 06:14:59 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/rtsold/probe.c,v 1.2.2.3 2001/07/03 11:02:16 ume Exp $ + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/queue.h> + +#include <net/if.h> +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include <net/if_var.h> +#endif /* __FreeBSD__ >= 3 */ + +#include <netinet/in.h> +#include <netinet6/in6_var.h> +#include <netinet/icmp6.h> +#include <netinet6/nd6.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> +#include <stdlib.h> + +#include "rtsold.h" + +static struct msghdr sndmhdr; +static struct iovec sndiov[2]; +static int probesock; +static void sendprobe __P((struct in6_addr *addr, int ifindex)); + + +int +probe_init() +{ + int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + static u_char *sndcmsgbuf = NULL; + + if (sndcmsgbuf == NULL && + (sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, "malloc failed"); + return(-1); + } + + if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); + return(-1); + } + +#ifndef __APPLE__ + /* make the socket send-only */ + if (shutdown(probesock, 0)) { + warnmsg(LOG_ERR, __FUNCTION__, "shutdown: %s", strerror(errno)); + return(-1); + } +#endif /* __APPLE__ */ + + /* initialize msghdr for sending packets */ + sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); + sndmhdr.msg_iov = sndiov; + sndmhdr.msg_iovlen = 1; + sndmhdr.msg_control = (caddr_t)sndcmsgbuf; + sndmhdr.msg_controllen = scmsglen; + + return(0); +} + +/* + * Probe if each router in the default router list is still alive. + */ +void +defrouter_probe(int ifindex) +{ + struct in6_drlist dr; + int s, i; + u_char ntopbuf[INET6_ADDRSTRLEN]; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); + return; + } + bzero(&dr, sizeof(dr)); + strlcpy(dr.ifname, "lo0", sizeof(dr.ifname)); /* dummy interface */ + if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGDRLST_IN6): %s", + strerror(errno)); + goto closeandend; + } + + for(i = 0; dr.defrouter[i].if_index && i < PRLSTSIZ; i++) { + if (ifindex && dr.defrouter[i].if_index == ifindex) { + /* sanity check */ + if (!IN6_IS_ADDR_LINKLOCAL(&dr.defrouter[i].rtaddr)) { + warnmsg(LOG_ERR, __FUNCTION__, + "default router list contains a " + "non-linklocal address(%s)", + inet_ntop(AF_INET6, + &dr.defrouter[i].rtaddr, + (char *)ntopbuf, INET6_ADDRSTRLEN)); + continue; /* ignore the address */ + } + sendprobe(&dr.defrouter[i].rtaddr, + dr.defrouter[i].if_index); + } + } + + closeandend: + close(s); + return; +} + +static void +sendprobe(struct in6_addr *addr, int ifindex) +{ + struct sockaddr_in6 sa6_probe; + struct in6_pktinfo *pi; + struct cmsghdr *cm; + u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];; + + bzero(&sa6_probe, sizeof(sa6_probe)); + sa6_probe.sin6_family = AF_INET6; + sa6_probe.sin6_len = sizeof(sa6_probe); + sa6_probe.sin6_addr = *addr; + + sndmhdr.msg_name = (caddr_t)&sa6_probe; + sndmhdr.msg_iov[0].iov_base = NULL; + sndmhdr.msg_iov[0].iov_len = 0; + + cm = CMSG_FIRSTHDR(&sndmhdr); + /* specify the outgoing interface */ + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pi = (struct in6_pktinfo *)CMSG_DATA(cm); + memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ + pi->ipi6_ifindex = ifindex; + + /* specify the hop limit of the packet for safety */ + { + int hoplimit = 1; + + cm = CMSG_NXTHDR(&sndmhdr, cm); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_HOPLIMIT; + cm->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); + } + + warnmsg(LOG_DEBUG, __FUNCTION__, "probe a router %s on %s", + inet_ntop(AF_INET6, addr, (char *)ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(ifindex, (char *)ifnamebuf)); + + if (sendmsg(probesock, &sndmhdr, 0)) + warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s", + if_indextoname(ifindex, (char *)ifnamebuf), strerror(errno)); + + return; +} diff --git a/network_cmds/rtsol.tproj/rtsock.c b/network_cmds/rtsol.tproj/rtsock.c new file mode 100644 index 0000000..e3f16e4 --- /dev/null +++ b/network_cmds/rtsol.tproj/rtsock.c @@ -0,0 +1,179 @@ +/* $KAME: rtsock.c,v 1.3 2000/10/10 08:46:45 itojun Exp $ */ +/* $FreeBSD: src/usr.sbin/rtsold/rtsock.c,v 1.1.2.1 2001/07/03 11:02:16 ume Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> + +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <stddef.h> +#include <err.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include "rtsold.h" + +#define ROUNDUP(a, size) \ + (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) + +#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ + ((caddr_t)(ap) + \ + ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(uint32_t)) \ + : sizeof(uint32_t))) + +#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/ +static int rtsock_input_ifannounce __P((int, struct rt_msghdr *, char *)); +#endif + +static struct { + u_char type; + size_t minlen; + int (*func) __P((int, struct rt_msghdr *, char *)); +} rtsock_dispatch[] = { +#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/ + { RTM_IFANNOUNCE, sizeof(struct if_announcemsghdr), + rtsock_input_ifannounce }, +#endif + { 0, 0UL, NULL }, +}; + +int +rtsock_open() +{ + + return socket(PF_ROUTE, SOCK_RAW, 0); +} + +int +rtsock_input(s) + int s; +{ + ssize_t n; + char msg[2048]; + char *lim, *next; + struct rt_msghdr *rtm; + int idx; + size_t len; + int ret = 0; + const size_t lenlim = + offsetof(struct rt_msghdr, rtm_msglen) + sizeof(rtm->rtm_msglen); + + n = read(s, msg, sizeof(msg)); + + lim = msg + n; + for (next = msg; next < lim; next += len) { + rtm = (struct rt_msghdr *)next; + if (lim - next < lenlim) + break; + len = rtm->rtm_msglen; + if (len < lenlim) + break; + + if (dflag > 1) { + warnmsg(LOG_INFO, __FUNCTION__, + "rtmsg type %d, len=%lu", rtm->rtm_type, + (u_long)len); + } + + for (idx = 0; rtsock_dispatch[idx].func; idx++) { + if (rtm->rtm_type != rtsock_dispatch[idx].type) + continue; + if (rtm->rtm_msglen < rtsock_dispatch[idx].minlen) { + warnmsg(LOG_INFO, __FUNCTION__, + "rtmsg type %d too short!", rtm->rtm_type); + continue; + } + + ret = (*rtsock_dispatch[idx].func)(s, rtm, lim); + break; + } + } + + return ret; +} + +#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/ +static int +rtsock_input_ifannounce(s, rtm, lim) + int s; + struct rt_msghdr *rtm; + char *lim; +{ + struct if_announcemsghdr *ifan; + struct ifinfo *ifinfo; + + ifan = (struct if_announcemsghdr *)rtm; + if ((char *)(ifan + 1) > lim) + return -1; + + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + /* + * XXX for NetBSD 1.5, interface index will monotonically be + * increased as new pcmcia card gets inserted. + * we may be able to do a name-based interface match, + * and call ifreconfig() to enable the interface again. + */ + warnmsg(LOG_INFO, __FUNCTION__, + "interface %s inserted", ifan->ifan_name); + break; + case IFAN_DEPARTURE: + warnmsg(LOG_WARNING, __FUNCTION__, + "interface %s removed", ifan->ifan_name); + ifinfo = find_ifinfo(ifan->ifan_index); + if (ifinfo) { + if (dflag > 1) { + warnmsg(LOG_INFO, __FUNCTION__, + "bring interface %s to DOWN state", + ifan->ifan_name); + } + ifinfo->state = IFS_DOWN; + } + break; + } + + return 0; +} +#endif diff --git a/network_cmds/rtsol.tproj/rtsol.8 b/network_cmds/rtsol.tproj/rtsol.8 new file mode 100644 index 0000000..30c3768 --- /dev/null +++ b/network_cmds/rtsol.tproj/rtsol.8 @@ -0,0 +1,224 @@ +.\" $KAME: rtsold.8,v 1.16 2000/10/15 13:19:05 itojun Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.sbin/rtsold/rtsold.8,v 1.1.2.5 2001/08/16 15:56:30 ru Exp $ +.\" +.Dd May 17, 1998 +.Dt RTSOLD 8 +.Os +.\" +.Sh NAME +.Nm rtsold +.Nd router solicitation daemon +.\" +.Sh SYNOPSIS +.Nm +.Op Fl dDfm1 +.Ar interface ... +.Nm +.Op Fl dDfm1 +.Fl a +.Nm rtsol +.Op Fl dD +.Ar interface ... +.Nm rtsol +.Op Fl dD +.Fl a +.\" +.Sh DESCRIPTION +.Nm +is the daemon program to send ICMPv6 Router Solicitation messages +on the specified interfaces. +If a node (re)attaches to a link, +.Nm +sends some Router Solicitations on the link destined to the link-local scope +all-routers multicast address to discover new routers +and to get non link-local addresses. +.Pp +.Nm +should be used on IPv6 hosts +(non-router nodes) +only. +.Pp +If you invoke the program as +.Nm rtsol , +it will transmit probes from the specified +.Ar interface , +without becoming a daemon. +In other words, +.Nm rtsol +behaves as +.Do +.Nm +.Fl f1 +.Ar interfaces +.Dc . +.Pp +Specifically, +.Nm +sends at most 3 Router Solicitations on an interface +after one of the following events: +.Pp +.Bl -bullet -compact +.It +Just after invocation of +.Nm +daemon. +.It +The interface is up after a temporary interface failure. +.Nm +detects such failures by periodically probing to see if the status +of the interface is active or not. +Note that some network cards and drivers do not allow the extraction +of link state. +In such cases, +.Nm +cannot detect the change of the interface status. +.It +Every 60 seconds if the +.Fl m +option is specified and the +.Nm +daemon cannot get the interface status. +This feature does not conform to the IPv6 neighbor discovery +specification, but is provided for mobile stations. +The default interval for router advertisements, which is on the order of 10 +minutes, is slightly long for mobile stations. +This feature is provided +for such stations so that they can find new routers as soon as possible +when they attach to another link. +.El +.Lp +Once +.Nm +has sent a Router Solicitation, and has received a valid Router Advertisement, +it refrains from sending additional solicitations on that interface, until +the next time one of the above events occurs. +.Lp +When sending a Router Solicitation on an interface, +.Nm +includes a Source Link-layer address option if the interface +has a link-layer address. +.Pp +Upon receipt of signal +.Dv SIGUSR1 , +.Nm +will dump the current internal state into +.Pa /var/run/rtsold.dump . +.\" +.Sh OPTIONS +.Bl -tag -width indent +.It Fl a +Autoprobe outgoing interface. +.Nm +will try to find a non-loopback, non-point-to-point, IPv6-capable interface. +If +.Nm +finds multiple interfaces, +.Nm +will exit with error. +.\" +.It Fl d +Enable debugging. +.It Fl D +Enable more debugging including the printing of internal timer information. +.It Fl f +.Fl f +prevents +.Nm +from becoming a daemon (foreground mode). +Warning messages are generated to standard error +instead of +.Xr syslog 3 . +.It Fl m +Enable mobility support. +If this option is specified, +.Nm +sends probing packets to default routers that have advertised Router +Advertisements +when the node (re)attaches to an interface. +Moreover, if the option is specified, +.Nm +periodically sends Router Solicitation on an interface that does not support +.Dv SIOCGIFMEDIA +ioctl. +.It Fl 1 +Perform only one probe. +Transmit Router Solicitation packets until at least one valid Router +Advertisement packet has arrived on each +.Ar interface , +then exit. +.El +.Sh DIAGNOSTICS +.Ex -std +.\" +.Sh FILES +.Bl -tag -width /var/run/rtsold.dump -compact +.It Pa /var/run/rtsold.pid +the pid of the currently running +.Nm . +.It Pa /var/run/rtsold.dump +dumps internal state on. +.El +.\" +.Sh SEE ALSO +.Xr rtadvd 8 , +.Xr sysctl 8 +.\" +.Sh HISTORY +The +.Nm +command is based on the +.Nm rtsol +command, which first appeared in WIDE/KAME IPv6 protocol stack kit. +.Nm rtsol +is now integrated into +.Xr rtsold 8 . +.\" +.Sh BUGS +In some operating systems, when a PCMCIA network card is removed +and reinserted, the corresponding interface index is changed. +However, +.Nm +assumes such changes will not occur, and always uses the index that +it got at invocation. As a result, +.Nm +may not work if you reinsert a network card. +In such a case, +.Nm +should be killed and restarted. +.Pp +The IPv6 autoconfiguration specification assumes a single-interface host. +You may see kernel error messages if you try to autoconfigure a host with +multiple interfaces. +Also, it seems contradictory for +.Nm +to accept multiple +.Ar interface +arguments. diff --git a/network_cmds/rtsol.tproj/rtsol.c b/network_cmds/rtsol.tproj/rtsol.c new file mode 100644 index 0000000..e1b9bff --- /dev/null +++ b/network_cmds/rtsol.tproj/rtsol.c @@ -0,0 +1,343 @@ +/* $KAME: rtsol.c,v 1.11 2000/08/13 06:14:59 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/rtsold/rtsol.c,v 1.1.2.3 2001/07/03 11:02:16 ume Exp $ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet/icmp6.h> + +#include <arpa/inet.h> + +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <err.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include "rtsold.h" + +#define ALLROUTER "ff02::2" + +static struct msghdr rcvmhdr; +static struct msghdr sndmhdr; +static struct iovec rcviov[2]; +static struct iovec sndiov[2]; +static struct sockaddr_in6 from; + +int rssock; + +static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6}; + +int +sockopen() +{ + int on; + struct icmp6_filter filt; + static u_char answer[1500]; + int rcvcmsglen, sndcmsglen; + static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; + + sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, + "malloc for receive msghdr failed"); + return(-1); + } + if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, + "malloc for send msghdr failed"); + return(-1); + } + memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6)); + if (inet_pton(AF_INET6, ALLROUTER, + &sin6_allrouters.sin6_addr.s6_addr) != 1) { + warnmsg(LOG_ERR, __FUNCTION__, "inet_pton failed for %s", + ALLROUTER); + return(-1); + } + + if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); + return(-1); + } + + /* specify to tell receiving interface */ + on = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVPKTINFO: %s", + strerror(errno)); + exit(1); + } +#else /* old adv. API */ + if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s", + strerror(errno)); + exit(1); + } +#endif + + on = 1; + /* specify to tell value of hoplimit field of received IP6 hdr */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVHOPLIMIT: %s", + strerror(errno)); + exit(1); + } +#else /* old adv. API */ + if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s", + strerror(errno)); + exit(1); + } +#endif + + /* specfiy to accept only router advertisements on the socket */ + ICMP6_FILTER_SETBLOCKALL(&filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); + if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) == -1) { + warnmsg(LOG_ERR, __FUNCTION__, "setsockopt(ICMP6_FILTER): %s", + strerror(errno)); + return(-1); + } + + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = (caddr_t)answer; + rcviov[0].iov_len = sizeof(answer); + rcvmhdr.msg_name = (caddr_t)&from; + rcvmhdr.msg_namelen = sizeof(from); + rcvmhdr.msg_iov = rcviov; + rcvmhdr.msg_iovlen = 1; + rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; + rcvmhdr.msg_controllen = rcvcmsglen; + + /* initialize msghdr for sending packets */ + sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); + sndmhdr.msg_iov = sndiov; + sndmhdr.msg_iovlen = 1; + sndmhdr.msg_control = (caddr_t)sndcmsgbuf; + sndmhdr.msg_controllen = sndcmsglen; + + return(rssock); +} + +void +sendpacket(struct ifinfo *ifinfo) +{ + int i; + struct cmsghdr *cm; + struct in6_pktinfo *pi; + + sndmhdr.msg_name = (caddr_t)&sin6_allrouters; + sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data; + sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen; + + cm = CMSG_FIRSTHDR(&sndmhdr); + /* specify the outgoing interface */ + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pi = (struct in6_pktinfo *)CMSG_DATA(cm); + memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ + pi->ipi6_ifindex = ifinfo->sdl->sdl_index; + + /* specify the hop limit of the packet */ + { + int hoplimit = 255; + + cm = CMSG_NXTHDR(&sndmhdr, cm); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_HOPLIMIT; + cm->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); + } + + warnmsg(LOG_DEBUG, + __FUNCTION__, "send RS on %s, whose state is %d", + ifinfo->ifname, ifinfo->state); + + i = sendmsg(rssock, &sndmhdr, 0); + + if (i < 0 || i != ifinfo->rs_datalen) { + /* + * ENETDOWN is not so serious, especially when using several + * network cards on a mobile node. We ignore it. + */ + if (errno != ENETDOWN || dflag > 0) + warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s", + ifinfo->ifname, strerror(errno)); + } + + /* update counter */ + ifinfo->probes++; +} + +void +rtsol_input(int s) +{ + int i; + int *hlimp = NULL; + struct icmp6_hdr *icp; + int ifindex = 0; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + struct ifinfo *ifi = NULL; + u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + + /* get message */ + if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { + warnmsg(LOG_ERR, __FUNCTION__, "recvmsg: %s", strerror(errno)); + return; + } + + /* extract optional information via Advanced API */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); + cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + ifindex = pi->ipi6_ifindex; + } + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *)CMSG_DATA(cm); + } + + if (ifindex == 0) { + warnmsg(LOG_ERR, + __FUNCTION__, "failed to get receiving interface"); + return; + } + if (hlimp == NULL) { + warnmsg(LOG_ERR, + __FUNCTION__, "failed to get receiving hop limit"); + return; + } + + if (i < sizeof(struct nd_router_advert)) { + warnmsg(LOG_ERR, + __FUNCTION__, "packet size(%d) is too short", i); + return; + } + + icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; + + if (icp->icmp6_type != ND_ROUTER_ADVERT) { + warnmsg(LOG_ERR, __FUNCTION__, + "invalid icmp type(%d) from %s on %s", icp->icmp6_type, + inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); + return; + } + + if (icp->icmp6_code != 0) { + warnmsg(LOG_ERR, __FUNCTION__, + "invalid icmp code(%d) from %s on %s", icp->icmp6_code, + inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); + return; + } + + if (*hlimp != 255) { + warnmsg(LOG_NOTICE, __FUNCTION__, + "invalid RA with hop limit(%d) from %s on %s", + *hlimp, + inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); + return; + } + + if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { + warnmsg(LOG_NOTICE, __FUNCTION__, + "invalid RA with non link-local source from %s on %s", + inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); + return; + } + + /* xxx: more validation? */ + + if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { + warnmsg(LOG_NOTICE, __FUNCTION__, + "received RA from %s on an unexpeced IF(%s)", + inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); + return; + } + + warnmsg(LOG_DEBUG, __FUNCTION__, + "received RA from %s on %s, state is %d", + inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, + INET6_ADDRSTRLEN), + ifi->ifname, ifi->state); + + ifi->racnt++; + + switch(ifi->state) { + case IFS_IDLE: /* should be ignored */ + case IFS_DELAY: /* right? */ + break; + case IFS_PROBE: + ifi->state = IFS_IDLE; + ifi->probes = 0; + rtsol_timer_update(ifi); + break; + } +} diff --git a/network_cmds/rtsol.tproj/rtsold.c b/network_cmds/rtsol.tproj/rtsold.c new file mode 100644 index 0000000..68923ec --- /dev/null +++ b/network_cmds/rtsol.tproj/rtsold.c @@ -0,0 +1,791 @@ +/* $KAME: rtsold.c,v 1.31 2001/05/22 06:03:06 jinmei Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/rtsold/rtsold.c,v 1.1.2.3 2001/07/03 11:02:16 ume Exp $ + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_dl.h> + +#include <netinet/in.h> +#include <netinet/icmp6.h> + +#include <signal.h> +#include <unistd.h> +#include <syslog.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <err.h> +#include <stdarg.h> +#include <ifaddrs.h> +#include "rtsold.h" + +struct ifinfo *iflist; +struct timeval tm_max = {0x7fffffff, 0x7fffffff}; +int aflag = 0; +int dflag = 0; +static int log_upto = 999; +static int fflag = 0; + +/* protocol constatns */ +#define MAX_RTR_SOLICITATION_DELAY 1 /* second */ +#define RTR_SOLICITATION_INTERVAL 4 /* seconds */ +#define MAX_RTR_SOLICITATIONS 3 /* times */ + +/* implementation dependent constants */ +#define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */ + +/* utility macros */ +/* a < b */ +#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) && \ + ((a).tv_usec < (b).tv_usec))) + +/* a <= b */ +#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) &&\ + ((a).tv_usec <= (b).tv_usec))) + +/* a == b */ +#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec)) + +int main __P((int argc, char *argv[])); + +/* static variables and functions */ +static int mobile_node = 0; +static int do_dump; +static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ +static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ + +static int ifconfig __P((char *ifname)); +#if 0 +static int ifreconfig __P((char *ifname)); +#endif +static int make_packet __P((struct ifinfo *ifinfo)); +static struct timeval *rtsol_check_timer __P((void)); +static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b, + struct timeval *result)); +static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b, + struct timeval *result)); + +static void rtsold_set_dump_file __P((void)); +static void usage __P((char *progname)); +static char **autoifprobe __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int s, rtsock, maxfd, ch; + int once = 0; + struct timeval *timeout; + struct fd_set fdset; + char *argv0; + char *opts; + + /* + * Initialization + */ + argv0 = argv[0]; + + /* get option */ + if (argv0 && argv0[strlen(argv0) - 1] != 'd') { + fflag = 1; + once = 1; + opts = "adD"; + } else + opts = "adDfm1"; + + while ((ch = getopt(argc, argv, opts)) != -1) { + switch (ch) { + case 'a': + aflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'D': + dflag = 2; + break; + case 'f': + fflag = 1; + break; + case 'm': + mobile_node = 1; + break; + case '1': + once = 1; + break; + default: + usage(argv0); + /*NOTREACHED*/ + } + } + argc -= optind; + argv += optind; + + if (aflag) { + int i; + + if (argc != 0) { + usage(argv0); + /*NOTREACHED*/ + } + + argv = autoifprobe(); + if (!argv) { + errx(1, "could not autoprobe interface"); + /*NOTREACHED*/ + } + + for (i = 0; argv[i]; i++) + ; + argc = i; + } + if (argc == 0) { + usage(argv0); + /*NOTREACHED*/ + } + + /* set log level */ + if (dflag == 0) + log_upto = LOG_NOTICE; + if (!fflag) { + char *ident; + ident = strrchr(argv0, '/'); + if (!ident) + ident = argv0; + else + ident++; + openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); + if (log_upto >= 0) + setlogmask(LOG_UPTO(log_upto)); + } + +#ifndef HAVE_ARC4RANDOM + /* random value initilization */ + srandom((u_long)time(NULL)); +#endif + + /* warn if accept_rtadv is down */ + if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) + warnx("kernel is configured not to accept RAs"); + /* warn if forwarding is up */ + if (getinet6sysctl(IPV6CTL_FORWARDING)) + warnx("kernel is configured as a router, not a host"); + + /* initialization to dump internal status to a file */ + if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) { + errx(1, "failed to set signal for dump status"); + /*NOTREACHED*/ + } + + /* + * Open a socket for sending RS and receiving RA. + * This should be done before calling ifinit(), since the function + * uses the socket. + */ + if ((s = sockopen()) < 0) { + errx(1, "failed to open a socket"); + /*NOTREACHED*/ + } + maxfd = s; + if ((rtsock = rtsock_open()) < 0) { + errx(1, "failed to open a socket"); + /*NOTREACHED*/ + } + if (rtsock > maxfd) + maxfd = rtsock; + + /* configuration per interface */ + if (ifinit()) { + errx(1, "failed to initilizatoin interfaces"); + /*NOTREACHED*/ + } + while (argc--) { + if (ifconfig(*argv)) { + errx(1, "failed to initialize %s", *argv); + /*NOTREACHED*/ + } + argv++; + } + + /* setup for probing default routers */ + if (probe_init()) { + errx(1, "failed to setup for probing routers"); + /*NOTREACHED*/ + } + + if (!fflag) + daemon(0, 0); /* act as a daemon */ + + /* dump the current pid */ + if (!once) { + pid_t pid = getpid(); + FILE *fp; + + if ((fp = fopen(pidfilename, "w")) == NULL) + warnmsg(LOG_ERR, __FUNCTION__, + "failed to open a log file(%s): %s", + pidfilename, strerror(errno)); + else { + fprintf(fp, "%d\n", pid); + fclose(fp); + } + } + + FD_ZERO(&fdset); + FD_SET(s, &fdset); + FD_SET(rtsock, &fdset); + while (1) { /* main loop */ + int e; + struct fd_set select_fd = fdset; + + if (do_dump) { /* SIGUSR1 */ + do_dump = 0; + rtsold_dump_file(dumpfilename); + } + + timeout = rtsol_check_timer(); + + if (once) { + struct ifinfo *ifi; + + /* if we have no timeout, we are done (or failed) */ + if (timeout == NULL) + break; + + /* if all interfaces have got RA packet, we are done */ + for (ifi = iflist; ifi; ifi = ifi->next) { + if (ifi->state != IFS_DOWN && ifi->racnt == 0) + break; + } + if (ifi == NULL) + break; + } + e = select(maxfd + 1, &select_fd, NULL, NULL, timeout); + if (e < 1) { + if (e < 0 && errno != EINTR) { + warnmsg(LOG_ERR, __FUNCTION__, "select: %s", + strerror(errno)); + } + continue; + } + + /* packet reception */ + if (FD_ISSET(rtsock, &select_fd)) + rtsock_input(rtsock); + if (FD_ISSET(s, &select_fd)) + rtsol_input(s); + } + /* NOTREACHED */ + + return 0; +} + +static int +ifconfig(char *ifname) +{ + struct ifinfo *ifinfo; + struct sockaddr_dl *sdl; + int flags; + + if ((sdl = if_nametosdl(ifname)) == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, + "failed to get link layer information for %s", ifname); + return(-1); + } + if (find_ifinfo(sdl->sdl_index)) { + warnmsg(LOG_ERR, __FUNCTION__, + "interface %s was already cofigured", ifname); + free(sdl); + return(-1); + } + + if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed"); + free(sdl); + return(-1); + } + memset(ifinfo, 0, sizeof(*ifinfo)); + ifinfo->sdl = sdl; + + strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); + + /* construct a router solicitation message */ + if (make_packet(ifinfo)) + goto bad; + + /* + * check if the interface is available. + * also check if SIOCGIFMEDIA ioctl is OK on the interface. + */ + ifinfo->mediareqok = 1; + ifinfo->active = interface_status(ifinfo); + if (!ifinfo->mediareqok) { + /* + * probe routers periodically even if the link status + * does not change. + */ + ifinfo->probeinterval = PROBE_INTERVAL; + } + + /* activate interface: interface_up returns 0 on success */ + flags = interface_up(ifinfo->ifname); + if (flags == 0) + ifinfo->state = IFS_DELAY; + else if (flags == IFS_TENTATIVE) + ifinfo->state = IFS_TENTATIVE; + else + ifinfo->state = IFS_DOWN; + + rtsol_timer_update(ifinfo); + + /* link into chain */ + if (iflist) + ifinfo->next = iflist; + iflist = ifinfo; + + return(0); + + bad: + free(ifinfo->sdl); + free(ifinfo); + return(-1); +} + +#if 0 +static int +ifreconfig(char *ifname) +{ + struct ifinfo *ifi, *prev; + int rv; + + prev = NULL; + for (ifi = iflist; ifi; ifi = ifi->next) { + if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) + break; + prev = ifi; + } + prev->next = ifi->next; + + rv = ifconfig(ifname); + + /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ + if (ifi->rs_data) + free(ifi->rs_data); + free(ifi->sdl); + free(ifi); + + return rv; +} +#endif + +struct ifinfo * +find_ifinfo(int ifindex) +{ + struct ifinfo *ifi; + + for (ifi = iflist; ifi; ifi = ifi->next) + if (ifi->sdl->sdl_index == ifindex) + return(ifi); + + return(NULL); +} + +static int +make_packet(struct ifinfo *ifinfo) +{ + char *buf; + struct nd_router_solicit *rs; + size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; + + if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) { + warnmsg(LOG_INFO, __FUNCTION__, + "link-layer address option has null length" + " on %s. Treat as not included.", ifinfo->ifname); + } + packlen += lladdroptlen; + ifinfo->rs_datalen = packlen; + + /* allocate buffer */ + if ((buf = malloc(packlen)) == NULL) { + warnmsg(LOG_ERR, __FUNCTION__, + "memory allocation failed for %s", ifinfo->ifname); + return(-1); + } + ifinfo->rs_data = (u_char *)buf; + + /* fill in the message */ + rs = (struct nd_router_solicit *)buf; + rs->nd_rs_type = ND_ROUTER_SOLICIT; + rs->nd_rs_code = 0; + rs->nd_rs_cksum = 0; + rs->nd_rs_reserved = 0; + buf += sizeof(*rs); + + /* fill in source link-layer address option */ + if (lladdroptlen) + lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf); + + return(0); +} + +static struct timeval * +rtsol_check_timer() +{ + static struct timeval returnval; + struct timeval now, rtsol_timer; + struct ifinfo *ifinfo; + int flags; + + gettimeofday(&now, NULL); + + rtsol_timer = tm_max; + + for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { + if (TIMEVAL_LEQ(ifinfo->expire, now)) { + if (dflag > 1) + warnmsg(LOG_DEBUG, __FUNCTION__, + "timer expiration on %s, " + "state = %d", ifinfo->ifname, + ifinfo->state); + + switch (ifinfo->state) { + case IFS_DOWN: + case IFS_TENTATIVE: + /* interface_up returns 0 on success */ + flags = interface_up(ifinfo->ifname); + if (flags == 0) + ifinfo->state = IFS_DELAY; + else if (flags == IFS_TENTATIVE) + ifinfo->state = IFS_TENTATIVE; + else + ifinfo->state = IFS_DOWN; + break; + case IFS_IDLE: + { + int oldstatus = ifinfo->active; + int probe = 0; + + ifinfo->active = + interface_status(ifinfo); + + if (oldstatus != ifinfo->active) { + warnmsg(LOG_DEBUG, __FUNCTION__, + "%s status is changed" + " from %d to %d", + ifinfo->ifname, + oldstatus, ifinfo->active); + probe = 1; + ifinfo->state = IFS_DELAY; + } + else if (ifinfo->probeinterval && + (ifinfo->probetimer -= + ifinfo->timer.tv_sec) <= 0) { + /* probe timer expired */ + ifinfo->probetimer = + ifinfo->probeinterval; + probe = 1; + ifinfo->state = IFS_PROBE; + } + + if (probe && mobile_node) + defrouter_probe(ifinfo->sdl->sdl_index); + break; + } + case IFS_DELAY: + ifinfo->state = IFS_PROBE; + sendpacket(ifinfo); + break; + case IFS_PROBE: + if (ifinfo->probes < MAX_RTR_SOLICITATIONS) + sendpacket(ifinfo); + else { + warnmsg(LOG_INFO, __FUNCTION__, + "No answer " + "after sending %d RSs", + ifinfo->probes); + ifinfo->probes = 0; + ifinfo->state = IFS_IDLE; + } + break; + } + rtsol_timer_update(ifinfo); + } + + if (TIMEVAL_LT(ifinfo->expire, rtsol_timer)) + rtsol_timer = ifinfo->expire; + } + + if (TIMEVAL_EQ(rtsol_timer, tm_max)) { + warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer"); + return(NULL); + } + else if (TIMEVAL_LT(rtsol_timer, now)) + /* this may occur when the interval is too small */ + returnval.tv_sec = returnval.tv_usec = 0; + else + TIMEVAL_SUB(&rtsol_timer, &now, &returnval); + + if (dflag > 1) + warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %ld:%08ld", + (long)returnval.tv_sec, (long)returnval.tv_usec); + + return(&returnval); +} + +void +rtsol_timer_update(struct ifinfo *ifinfo) +{ +#define MILLION 1000000 +#define DADRETRY 10 /* XXX: adhoc */ + long interval; + struct timeval now; + + bzero(&ifinfo->timer, sizeof(ifinfo->timer)); + + switch (ifinfo->state) { + case IFS_DOWN: + case IFS_TENTATIVE: + if (++ifinfo->dadcount > DADRETRY) { + ifinfo->dadcount = 0; + ifinfo->timer.tv_sec = PROBE_INTERVAL; + } + else + ifinfo->timer.tv_sec = 1; + break; + case IFS_IDLE: + if (mobile_node) { + /* XXX should be configurable */ + ifinfo->timer.tv_sec = 3; + } + else + ifinfo->timer = tm_max; /* stop timer(valid?) */ + break; + case IFS_DELAY: +#ifndef HAVE_ARC4RANDOM + interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); +#else + interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); +#endif + ifinfo->timer.tv_sec = interval / MILLION; + ifinfo->timer.tv_usec = interval % MILLION; + break; + case IFS_PROBE: + if (ifinfo->probes < MAX_RTR_SOLICITATIONS) + ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL; + else { + /* + * After sending MAX_RTR_SOLICITATIONS solicitations, + * we're just waiting for possible replies; there + * will be no more solicatation. Thus, we change + * the timer value to MAX_RTR_SOLICITATION_DELAY based + * on RFC 2461, Section 6.3.7. + */ + ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; + } + break; + default: + warnmsg(LOG_ERR, __FUNCTION__, + "illegal interface state(%d) on %s", + ifinfo->state, ifinfo->ifname); + return; + } + + /* reset the timer */ + if (TIMEVAL_EQ(ifinfo->timer, tm_max)) { + ifinfo->expire = tm_max; + warnmsg(LOG_DEBUG, __FUNCTION__, + "stop timer for %s", ifinfo->ifname); + } + else { + gettimeofday(&now, NULL); + TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire); + + if (dflag > 1) + warnmsg(LOG_DEBUG, __FUNCTION__, + "set timer for %s to %d:%d", ifinfo->ifname, + (int)ifinfo->timer.tv_sec, + (int)ifinfo->timer.tv_usec); + } + +#undef MILLION +} + +/* timer related utility functions */ +#define MILLION 1000000 + +/* result = a + b */ +static void +TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec + b->tv_usec) < MILLION) { + result->tv_usec = l; + result->tv_sec = a->tv_sec + b->tv_sec; + } + else { + result->tv_usec = l - MILLION; + result->tv_sec = a->tv_sec + b->tv_sec + 1; + } +} + +/* + * result = a - b + * XXX: this function assumes that a >= b. + */ +void +TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec - b->tv_usec) >= 0) { + result->tv_usec = l; + result->tv_sec = a->tv_sec - b->tv_sec; + } + else { + result->tv_usec = MILLION + l; + result->tv_sec = a->tv_sec - b->tv_sec - 1; + } +} + +static void +rtsold_set_dump_file() +{ + do_dump = 1; +} + +static void +usage(char *progname) +{ + if (progname && progname[strlen(progname) - 1] != 'd') { + fprintf(stderr, "usage: rtsol [-dD] interfaces...\n"); + fprintf(stderr, "usage: rtsol [-dD] -a\n"); + } else { + fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n"); + fprintf(stderr, "usage: rtsold [-dDfm1] -a\n"); + } + exit(1); +} + +void +#if __STDC__ +warnmsg(int priority, const char *func, const char *msg, ...) +#else +warnmsg(priority, func, msg, va_alist) + int priority; + const char *func; + const char *msg; + va_dcl +#endif +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, msg); + if (fflag) { + if (priority <= log_upto) { + (void)vfprintf(stderr, msg, ap); + (void)fprintf(stderr, "\n"); + } + } else { + snprintf(buf, sizeof(buf), "<%s> %s", func, msg); + msg = buf; + vsyslog(priority, msg, ap); + } + va_end(ap); +} + +static char ** +autoifprobe() +{ +#ifndef HAVE_GETIFADDRS + errx(1, "-a is not available with the configuration"); +#else + static char ifname[IFNAMSIZ + 1]; + static char *argv[2]; + struct ifaddrs *ifap, *ifa, *target; + + if (getifaddrs(&ifap) != 0) + return NULL; + + target = NULL; + /* find an ethernet */ + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_UP) == 0) + continue; + if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) + continue; + if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) + continue; + if ((ifa->ifa_flags & IFF_MULTICAST) == 0) + continue; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0) + continue; + + if (!target) + target = ifa; + else { + /* if we find multiple candidates, failure. */ + if (dflag > 1) + warnx("multiple interfaces found"); + target = NULL; + break; + } + } + + if (target) { + strlcpy(ifname, target->ifa_name, sizeof(ifname)); + argv[0] = ifname; + argv[1] = NULL; + + if (dflag > 0) + warnx("probing %s", argv[0]); + } + freeifaddrs(ifap); + if (target) + return argv; + else + return (char **)NULL; +#endif +} diff --git a/network_cmds/rtsol.tproj/rtsold.h b/network_cmds/rtsol.tproj/rtsold.h new file mode 100644 index 0000000..fdda539 --- /dev/null +++ b/network_cmds/rtsol.tproj/rtsold.h @@ -0,0 +1,96 @@ +/* $KAME: rtsold.h,v 1.11 2000/10/10 06:18:04 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/rtsold/rtsold.h,v 1.1.2.3 2001/07/03 11:02:16 ume Exp $ + */ + +struct ifinfo { + struct ifinfo *next; /* pointer to the next interface */ + + struct sockaddr_dl *sdl; /* link-layer address */ + char ifname[IF_NAMESIZE]; /* interface name */ + int active; /* interface status */ + int probeinterval; /* interval of probe timer(if necessary) */ + int probetimer; /* rest of probe timer */ + int mediareqok; /* wheter the IF supports SIOCGIFMEDIA */ + int state; + int probes; + int dadcount; + struct timeval timer; + struct timeval expire; + int errors; /* # of errors we've got - detect wedge */ + + int racnt; /* total # of valid RAs it have got */ + + size_t rs_datalen; + u_char *rs_data; +}; + +/* per interface status */ +#define IFS_IDLE 0 +#define IFS_DELAY 1 +#define IFS_PROBE 2 +#define IFS_DOWN 3 +#define IFS_TENTATIVE 4 +#define IFS_OPTIMISTIC 5 + +/* rtsold.c */ +extern struct timeval tm_max; +extern int dflag; +struct ifinfo *find_ifinfo __P((int ifindex)); +void rtsol_timer_update __P((struct ifinfo *ifinfo)); +extern void warnmsg __P((int, const char *, const char *, ...)) + __attribute__((__format__(__printf__, 3, 4))); + +/* if.c */ +extern int ifinit __P((void)); +extern int interface_up __P((char *name)); +extern int interface_status __P((struct ifinfo*)); +extern int lladdropt_length __P((struct sockaddr_dl *sdl)); +extern void lladdropt_fill __P((struct sockaddr_dl *sdl, + struct nd_opt_hdr *ndopt)); +extern struct sockaddr_dl *if_nametosdl __P((char *name)); +extern int getinet6sysctl __P((int code)); + +/* rtsol.c */ +extern int sockopen __P((void)); +extern void sendpacket __P((struct ifinfo *ifinfo)); +extern void rtsol_input __P((int s)); + +/* probe.c */ +extern int probe_init __P((void)); +extern void defrouter_probe __P((int ifindex)); + +/* dump.c */ +extern void rtsold_dump_file __P((char *)); + +/* rtsock.c */ +extern int rtsock_open __P((void)); +extern int rtsock_input __P((int)); diff --git a/network_cmds/spray.tproj/spray.8 b/network_cmds/spray.tproj/spray.8 new file mode 100644 index 0000000..0f61422 --- /dev/null +++ b/network_cmds/spray.tproj/spray.8 @@ -0,0 +1,74 @@ +.\" +.\" Copyright (c) 1994 James A. Jegers +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd July 10, 1995 +.Dt SPRAY 8 +.Os +.Sh NAME +.Nm spray +.Nd send many packets to host +.Sh SYNOPSIS +.Nm spray +.Op Fl c Ar count +.Op Fl d Ar delay +.Op Fl l Ar length +.Ar host +\&... +.Sh DESCRIPTION +.Nm Spray +sends multiple RPC packets to +.Ar host +and records how many of them were correctly received and how long it took. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl c Ar count +Send +.Ar count +packets. +.It Fl d Ar delay +Pause +.Ar delay +microseconds between sending each packet. +.It Fl l Ar length +Set the length of the packet that holds the RPC call message to +.Ar length +bytes. +Not all values of +.Ar length +are possible because RPC data is encoded using XDR. +.Nm Spray +rounds up to the nearest possible value. +.El +.Pp +.Nm Spray +is intended for use in network testing, measurement, and management. +This command +.Bf -emphasis +can be very hard on a network and should be used with caution. +.Ef +.Pp +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ifconfig 8 , +.Xr ping 8 diff --git a/network_cmds/spray.tproj/spray.c b/network_cmds/spray.tproj/spray.c new file mode 100644 index 0000000..70fd8db --- /dev/null +++ b/network_cmds/spray.tproj/spray.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: spray.c,v 1.2 2006/02/07 06:22:44 lindak Exp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rpc/rpc.h> +#include <rpcsvc/spray.h> + +#ifndef SPRAYOVERHEAD +#define SPRAYOVERHEAD 86 +#endif + +void usage (); +void print_xferstats (); + +/* spray buffer */ +char spray_buffer[SPRAYMAX]; + +/* RPC timeouts */ +struct timeval NO_DEFAULT = { -1, -1 }; +struct timeval ONE_WAY = { 0, 0 }; +struct timeval TIMEOUT = { 25, 0 }; + +int +main(argc, argv) + int argc; + char **argv; +{ + char *progname; + spraycumul host_stats; + sprayarr host_array; + CLIENT *cl; + int c; + int i; + int count = 0; + int delay = 0; + int length = 0; + double xmit_time; /* time to receive data */ + + progname = *argv; + while ((c = getopt(argc, argv, "c:d:l:")) != -1) { + switch (c) { + case 'c': + count = atoi(optarg); + break; + case 'd': + delay = atoi(optarg); + break; + case 'l': + length = atoi(optarg); + break; + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (argc != 1) { + usage(); + /* NOTREACHED */ + } + + + /* Correct packet length. */ + if (length > SPRAYMAX) { + length = SPRAYMAX; + } else if (length < SPRAYOVERHEAD) { + length = SPRAYOVERHEAD; + } else { + /* The RPC portion of the packet is a multiple of 32 bits. */ + length -= SPRAYOVERHEAD - 3; + length &= ~3; + length += SPRAYOVERHEAD; + } + + + /* + * The default value of count is the number of packets required + * to make the total stream size 100000 bytes. + */ + if (!count) { + count = 100000 / length; + } + + /* Initialize spray argument */ + host_array.sprayarr_len = length - SPRAYOVERHEAD; + host_array.sprayarr_val = spray_buffer; + + + /* create connection with server */ + cl = clnt_create(*argv, SPRAYPROG, SPRAYVERS, "udp"); + if (cl == NULL) { + clnt_pcreateerror(progname); + exit(1); + } + + + /* + * For some strange reason, RPC 4.0 sets the default timeout, + * thus timeouts specified in clnt_call() are always ignored. + * + * The following (undocumented) hack resets the internal state + * of the client handle. + */ + clnt_control(cl, CLSET_TIMEOUT, (char *)&NO_DEFAULT); + + + /* Clear server statistics */ + if (clnt_call(cl, SPRAYPROC_CLEAR, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) { + clnt_perror(cl, progname); + exit(1); + } + + + /* Spray server with packets */ + printf ("sending %d packets of lnth %d to %s ...", count, length, *argv); + fflush (stdout); + + for (i = 0; i < count; i++) { + clnt_call(cl, SPRAYPROC_SPRAY, (xdrproc_t)xdr_sprayarr, &host_array, (xdrproc_t)xdr_void, NULL, ONE_WAY); + + if (delay) { + usleep(delay); + } + } + + + /* Collect statistics from server */ + if (clnt_call(cl, SPRAYPROC_GET, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS) { + clnt_perror(cl, progname); + exit(1); + } + + xmit_time = host_stats.clock.sec + + (host_stats.clock.usec / 1000000.0); + + printf ("\n\tin %.2f seconds elapsed time\n", xmit_time); + + + /* report dropped packets */ + if (host_stats.counter != count) { + int packets_dropped = count - host_stats.counter; + + printf("\t%d packets (%.2f%%) dropped\n", + packets_dropped, + 100.0 * packets_dropped / count ); + } else { + printf("\tno packets dropped\n"); + } + + printf("Sent:"); + print_xferstats(count, length, xmit_time); + + printf("Rcvd:"); + print_xferstats(host_stats.counter, length, xmit_time); + + exit (0); +} + + +void +print_xferstats(packets, packetlen, xfertime) + int packets; + int packetlen; + double xfertime; +{ + int datalen; + double pps; /* packets per second */ + double bps; /* bytes per second */ + + datalen = packets * packetlen; + pps = packets / xfertime; + bps = datalen / xfertime; + + printf("\t%.0f packets/sec, ", pps); + + if (bps >= 1024) + printf ("%.1fK ", bps / 1024); + else + printf ("%.0f ", bps); + + printf("bytes/sec\n"); +} + + +void +usage () +{ + fprintf(stderr, "usage: spray [-c count] [-l length] [-d delay] host\n"); + exit(1); +} diff --git a/network_cmds/spray.tproj/spray.x b/network_cmds/spray.tproj/spray.x new file mode 100644 index 0000000..5d2f883 --- /dev/null +++ b/network_cmds/spray.tproj/spray.x @@ -0,0 +1,24 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <rpcsvc/spray.x> diff --git a/network_cmds/traceroute.tproj/README b/network_cmds/traceroute.tproj/README new file mode 100644 index 0000000..6d33c6c --- /dev/null +++ b/network_cmds/traceroute.tproj/README @@ -0,0 +1,126 @@ +Tue Dec 27 06:24:24 PST 1988 + +Traceroute is a system administrators utility to trace the route +ip packets from the current system take in getting to some +destination system. See the comments at the front of the +program for a description of its use. + +This program + + a) can only be run by root (it uses raw ip sockets). + + b) REQUIRES A KERNEL MOD to the raw ip output code to run. + +If you want to hack on your kernel, my modified version of the +routine rip_output (in file /sys/netinet/raw_ip.c) is attached. +This code may or may not resemble the code in your kernel. +It may offer you a place to start but I make no promises. +If you do hack your kernel, remember to test everything that uses +raw ip sockets (e.g., ping and egpup/gated) & make sure they still +work. I wish you the best of luck and you're on your own. + +If your system has the ttl bug mentioned in the source, you +might want to fix it while you're in the kernel. (This bug +appears in all releases of BSD up to but not including 4.3tahoe. +If your version of netinet/ip_icmp.c is any earlier than 7.3 +(April, '87), it has the bug.) The fix is just to add the line + ip->ip_ttl = MAXTTL; +after the line + ip->ip_src = t; +(or anywhere before the call to icmp_send) in routine icmp_reflect. + +If you're running this on a pre-4.3bsd system (e.g., Sun 3.x, +Ultrix) that strips ip headers from icmp messages, add -DARCHAIC +to CFLAGS in the Makefile. Also note that rip_output contains +a conditional for a 4.2/4.3 change in the location of a raw +socket's protocol number. I've checked this under 4.3 & Sun OS +3.5 but you should double-check your system to make sure the +appropriate branch of the #if is taken (check the line that +assigned to ip->ip_p in your system's original rip_output). + +A couple of awk programs to massage the traceroute output are +included. "mean.awk" and "median.awk" compute the mean and median +time to each hop, respectively. I've found that something like + + traceroute -q 7 foo.somewhere >t + awk -f median.awk t | graph + +can give you a quick picture of the bad spots on a long +path (median is usually a better noise filter than mean). + +Enjoy. + + - Van Jacobson (van@helios.ee.lbl.gov) + +-------------------- rip_output from /sys/netinet/raw_ip.c +rip_output(m, so) + register struct mbuf *m; + struct socket *so; +{ + register struct ip *ip; + int error; + struct rawcb *rp = sotorawcb(so); + struct sockaddr_in *sin; +#if BSD>=43 + short proto = rp->rcb_proto.sp_protocol; +#else + short proto = so->so_proto->pr_protocol; +#endif + /* + * if the protocol is IPPROTO_RAW, the user handed us a + * complete IP packet. Otherwise, allocate an mbuf for a + * header and fill it in as needed. + */ + if (proto != IPPROTO_RAW) { + /* + * Calculate data length and get an mbuf + * for IP header. + */ + int len = 0; + struct mbuf *m0; + + for (m0 = m; m; m = m->m_next) + len += m->m_len; + + m = m_get(M_DONTWAIT, MT_HEADER); + if (m == 0) { + m = m0; + error = ENOBUFS; + goto bad; + } + m->m_off = MMAXOFF - sizeof(struct ip); + m->m_len = sizeof(struct ip); + m->m_next = m0; + + ip = mtod(m, struct ip *); + ip->ip_tos = 0; + ip->ip_off = 0; + ip->ip_p = proto; + ip->ip_len = sizeof(struct ip) + len; + ip->ip_ttl = MAXTTL; + } else + ip = mtod(m, struct ip *); + + if (rp->rcb_flags & RAW_LADDR) { + sin = (struct sockaddr_in *)&rp->rcb_laddr; + if (sin->sin_family != AF_INET) { + error = EAFNOSUPPORT; + goto bad; + } + ip->ip_src.s_addr = sin->sin_addr.s_addr; + } else + ip->ip_src.s_addr = 0; + + ip->ip_dst = ((struct sockaddr_in *)&rp->rcb_faddr)->sin_addr; + +#if BSD>=43 + return (ip_output(m, rp->rcb_options, &rp->rcb_route, + (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST)); +#else + return (ip_output(m, (struct mbuf *)0, &rp->rcb_route, + (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST)); +#endif +bad: + m_freem(m); + return (error); +} diff --git a/network_cmds/traceroute.tproj/as.c b/network_cmds/traceroute.tproj/as.c new file mode 100644 index 0000000..eb96851 --- /dev/null +++ b/network_cmds/traceroute.tproj/as.c @@ -0,0 +1,242 @@ +/* $FreeBSD: src/contrib/traceroute/as.c,v 1.1 2008/02/20 23:29:52 rpaulo Exp $ */ +/* $NetBSD: as.c,v 1.1 2001/11/04 23:14:36 atatat Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Brown. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <err.h> +#include <stdio.h> + +#include "as.h" + +#define DEFAULT_AS_SERVER "whois.radb.net" +#undef AS_DEBUG_FILE + +struct aslookup { + FILE *as_f; +#ifdef AS_DEBUG_FILE + FILE *as_debug; +#endif /* AS_DEBUG_FILE */ +}; + +void * +as_setup(server) + char *server; +{ + struct aslookup *asn; + struct hostent *he = NULL; + struct servent *se; + struct sockaddr_in in; + FILE *f; + int s; + + if (server == NULL) + server = DEFAULT_AS_SERVER; + + (void)memset(&in, 0, sizeof(in)); + in.sin_family = AF_INET; + in.sin_len = sizeof(in); + if ((se = getservbyname("whois", "tcp")) == NULL) { + warnx("warning: whois/tcp service not found"); + in.sin_port = ntohs(43); + } else + in.sin_port = se->s_port; + + if (inet_aton(server, &in.sin_addr) == 0 && + ((he = gethostbyname(server)) == NULL || + he->h_addr == NULL)) { + warnx("%s: %s", server, hstrerror(h_errno)); + return (NULL); + } + + if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + warn("socket"); + return (NULL); + } + + do { + if (he != NULL) { + memcpy(&in.sin_addr, he->h_addr, he->h_length); + he->h_addr_list++; + } + if (connect(s, (struct sockaddr *)&in, sizeof(in)) == 0) + break; + if (he == NULL || he->h_addr == NULL) { + close(s); + s = -1; + break; + } + } while (1); + + if (s == -1) { + warn("connect"); + return (NULL); + } + + f = fdopen(s, "r+"); + (void)fprintf(f, "!!\n"); + (void)fflush(f); + + asn = malloc(sizeof(struct aslookup)); + if (asn == NULL) + (void)fclose(f); + else + asn->as_f = f; + +#ifdef AS_DEBUG_FILE + asn->as_debug = fopen(AS_DEBUG_FILE, "w"); + if (asn->as_debug) { + (void)fprintf(asn->as_debug, ">> !!\n"); + (void)fflush(asn->as_debug); + } +#endif /* AS_DEBUG_FILE */ + + return (asn); +} + +int +as_lookup(_asn, addr) + void *_asn; + struct in_addr *addr; +{ + struct aslookup *asn = _asn; + char buf[1024]; + int as, rc, dlen; + + as = rc = dlen = 0; + (void)fprintf(asn->as_f, "!r%s/32,l\n", inet_ntoa(*addr)); + (void)fflush(asn->as_f); + +#ifdef AS_DEBUG_FILE + if (asn->as_debug) { + (void)fprintf(asn->as_debug, ">> !r%s/32,l\n", + inet_ntoa(*addr)); + (void)fflush(asn->as_debug); + } +#endif /* AS_DEBUG_FILE */ + + while (fgets(buf, sizeof(buf), asn->as_f) != NULL) { + buf[sizeof(buf) - 1] = '\0'; + +#ifdef AS_DEBUG_FILE + if (asn->as_debug) { + (void)fprintf(asn->as_debug, "<< %s", buf); + (void)fflush(asn->as_debug); + } +#endif /* AS_DEBUG_FILE */ + + if (rc == 0) { + rc = buf[0]; + switch (rc) { + case 'A': + /* A - followed by # bytes of answer */ + sscanf(buf, "A%d\n", &dlen); +#ifdef AS_DEBUG_FILE + if (asn->as_debug) { + (void)fprintf(asn->as_debug, + "dlen: %d\n", dlen); + (void)fflush(asn->as_debug); + } +#endif /* AS_DEBUG_FILE */ + break; + case 'C': + case 'D': + case 'E': + case 'F': + /* C - no data returned */ + /* D - key not found */ + /* E - multiple copies of key */ + /* F - some other error */ + break; + } + if (rc == 'A') + /* skip to next input line */ + continue; + } + + if (dlen == 0) + /* out of data, next char read is end code */ + rc = buf[0]; + if (rc != 'A') + /* either an error off the bat, or a done code */ + break; + + /* data received, thank you */ + dlen -= strlen(buf); + + /* origin line is the interesting bit */ + if (as == 0 && strncasecmp(buf, "origin:", 7) == 0) { + sscanf(buf + 7, " AS%d", &as); +#ifdef AS_DEBUG_FILE + if (asn->as_debug) { + (void)fprintf(asn->as_debug, "as: %d\n", as); + (void)fflush(asn->as_debug); + } +#endif /* AS_DEBUG_FILE */ + } + } + + return (as); +} + +void +as_shutdown(_asn) + void *_asn; +{ + struct aslookup *asn = _asn; + + (void)fprintf(asn->as_f, "!q\n"); + (void)fclose(asn->as_f); + +#ifdef AS_DEBUG_FILE + if (asn->as_debug) { + (void)fprintf(asn->as_debug, ">> !q\n"); + (void)fclose(asn->as_debug); + } +#endif /* AS_DEBUG_FILE */ + + free(asn); +} diff --git a/network_cmds/traceroute.tproj/as.h b/network_cmds/traceroute.tproj/as.h new file mode 100644 index 0000000..a4c6f47 --- /dev/null +++ b/network_cmds/traceroute.tproj/as.h @@ -0,0 +1,42 @@ +/* $FreeBSD: src/contrib/traceroute/as.h,v 1.1 2008/02/20 23:29:52 rpaulo Exp $ */ +/* $NetBSD: as.h,v 1.1 2001/11/04 23:14:36 atatat Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Brown. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void *as_setup __P((char *)); +int as_lookup __P((void *, struct in_addr *)); +void as_shutdown __P((void *)); diff --git a/network_cmds/traceroute.tproj/findsaddr-socket.c b/network_cmds/traceroute.tproj/findsaddr-socket.c new file mode 100644 index 0000000..01dcef1 --- /dev/null +++ b/network_cmds/traceroute.tproj/findsaddr-socket.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/contrib/traceroute/findsaddr-socket.c,v 1.2 2002/07/30 04:49:13 fenner Exp $ + */ + +/* XXX Yes this is WAY too complicated */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#include <sys/time.h> /* concession to AIX */ + +#if __STDC__ +struct mbuf; +struct rtentry; +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "findsaddr.h" + +#ifdef HAVE_SOCKADDR_SA_LEN +#define SALEN(sa) ((sa)->sa_len) +#else +#define SALEN(sa) salen(sa) +#endif + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ +#endif + +struct rtmsg { + struct rt_msghdr rtmsg; + u_char data[512]; +}; + +static struct rtmsg rtmsg = { + { 0, RTM_VERSION, RTM_GET, 0, + RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC, + RTA_DST | RTA_IFA, 0, 0, 0, 0, 0, { 0 } }, + { 0 } +}; + +#ifndef HAVE_SOCKADDR_SA_LEN +static int salen(struct sockaddr *); +#endif + +/* + * Return the source address for the given destination address + */ +const char * +findsaddr(register const struct sockaddr_in *to, + register struct sockaddr_in *from) +{ + register struct rt_msghdr *rp; + register u_char *cp; + + register struct sockaddr_in *sp, *ifa; + register struct sockaddr *sa; + register int s, size, cc, seq, i; + register pid_t pid; + static char errbuf[512]; + + s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (s < 0) { + snprintf(errbuf, sizeof(errbuf), "socket: %.128s", strerror(errno)); + return (errbuf); + } + + seq = 0; + pid = getpid(); + + rp = &rtmsg.rtmsg; + rp->rtm_seq = ++seq; + cp = (u_char *)(rp + 1); + + sp = (struct sockaddr_in *)cp; + *sp = *to; + cp += roundup(SALEN((struct sockaddr *)sp), sizeof(u_int32_t)); + + size = cp - (u_char *)rp; + rp->rtm_msglen = size; + + cc = write(s, (char *)rp, size); + if (cc < 0) { + snprintf(errbuf, sizeof(errbuf), "write: %.128s", strerror(errno)); + close(s); + return (errbuf); + } + if (cc != size) { + snprintf(errbuf, sizeof(errbuf), "short write (%d != %d)", cc, size); + close(s); + return (errbuf); + } + + size = sizeof(rtmsg); + do { + memset(rp, 0, size); + cc = read(s, (char *)rp, size); + if (cc < 0) { + snprintf(errbuf, sizeof(errbuf), "read: %.128s", strerror(errno)); + close(s); + return (errbuf); + } + + } while (rp->rtm_seq != seq || rp->rtm_pid != pid); + close(s); + + + if (rp->rtm_version != RTM_VERSION) { + snprintf(errbuf, sizeof(errbuf), "bad version %d", rp->rtm_version); + return (errbuf); + } + if (rp->rtm_msglen > cc) { + snprintf(errbuf, sizeof(errbuf), "bad msglen %d > %d", rp->rtm_msglen, cc); + return (errbuf); + } + if (rp->rtm_errno != 0) { + snprintf(errbuf, sizeof(errbuf), "rtm_errno: %.128s", strerror(rp->rtm_errno)); + return (errbuf); + } + + /* Find the interface sockaddr */ + cp = (u_char *)(rp + 1); + for (i = 1; i != 0; i <<= 1) + if ((i & rp->rtm_addrs) != 0) { + sa = (struct sockaddr *)cp; + switch (i) { + + case RTA_IFA: + if (sa->sa_family == AF_INET) { + ifa = (struct sockaddr_in *)cp; + if (ifa->sin_addr.s_addr != 0) { + *from = *ifa; + return (NULL); + } + } + break; + + default: + break; + /* empty */ + } + + if (SALEN(sa) == 0) + cp += sizeof (u_int32_t); + else + cp += roundup(SALEN(sa), sizeof (u_int32_t)); + } + + return ("failed!"); +} + +#ifndef HAVE_SOCKADDR_SA_LEN +static int +salen(struct sockaddr *sa) +{ + switch (sa->sa_family) { + + case AF_INET: + return (sizeof(struct sockaddr_in)); + + case AF_LINK: + return (sizeof(struct sockaddr_dl)); + + default: + return (sizeof(struct sockaddr)); + } +} +#endif diff --git a/network_cmds/traceroute.tproj/findsaddr.h b/network_cmds/traceroute.tproj/findsaddr.h new file mode 100644 index 0000000..49ed9e1 --- /dev/null +++ b/network_cmds/traceroute.tproj/findsaddr.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Id: findsaddr.h,v 1.2 2004/08/08 00:27:54 lindak Exp $ (LBL) + */ +const char *findsaddr(const struct sockaddr_in *, struct sockaddr_in *); diff --git a/network_cmds/traceroute.tproj/gnuc.h b/network_cmds/traceroute.tproj/gnuc.h new file mode 100644 index 0000000..f13c0be --- /dev/null +++ b/network_cmds/traceroute.tproj/gnuc.h @@ -0,0 +1,43 @@ +/* @(#) $Header: /Volumes/george/fs-svn/network_cmds/traceroute.tproj/gnuc.h,v 1.2 2004/08/08 00:27:54 lindak Exp $ (LBL) */ + +/* Define __P() macro, if necessary */ +#ifndef __P +#if __STDC__ +#define __P(protos) protos +#else +#define __P(protos) () +#endif +#endif + +/* inline foo */ +#ifdef __GNUC__ +#define inline __inline +#else +#define inline +#endif + +/* + * Handle new and old "dead" routine prototypes + * + * For example: + * + * __dead void foo(void) __attribute__((volatile)); + * + */ +#ifdef __GNUC__ +#ifndef __dead +#define __dead volatile +#endif +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +#ifndef __attribute__ +#define __attribute__(args) +#endif +#endif +#else +#ifndef __dead +#define __dead +#endif +#ifndef __attribute__ +#define __attribute__(args) +#endif +#endif diff --git a/network_cmds/traceroute.tproj/ifaddrlist.c b/network_cmds/traceroute.tproj/ifaddrlist.c new file mode 100644 index 0000000..17b1026 --- /dev/null +++ b/network_cmds/traceroute.tproj/ifaddrlist.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1997, 1998, 1999, 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#include <sys/time.h> /* concession to AIX */ + +#if __STDC__ +struct mbuf; +struct rtentry; +#endif + +#include <net/if.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <errno.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "ifaddrlist.h" + +/* + * Return the interface list + */ +int +ifaddrlist(register struct ifaddrlist **ipaddrp, register char *errbuf, size_t errbuflen) +{ + register int fd, nipaddr; +#ifdef HAVE_SOCKADDR_SA_LEN + register int n; +#endif + register struct ifreq *ifrp, *ifend, *ifnext, *mp; + register struct sockaddr_in *sin; + register struct ifaddrlist *al; + struct ifconf ifc; + struct ifreq ibuf[(32 * 1024) / sizeof(struct ifreq)], ifr; +#define MAX_IPADDR (sizeof(ibuf) / sizeof(ibuf[0])) + static struct ifaddrlist ifaddrlist[MAX_IPADDR]; + char device[sizeof(ifr.ifr_name) + 1]; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + (void)snprintf(errbuf, errbuflen, "socket: %s", strerror(errno)); + return (-1); + } + ifc.ifc_len = sizeof(ibuf); + ifc.ifc_buf = (caddr_t)ibuf; + + if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + if (errno == EINVAL) + (void)snprintf(errbuf, sizeof(errbuf), + "SIOCGIFCONF: ifreq struct too small (%d bytes)", + (int)sizeof(ibuf)); + else + (void)snprintf(errbuf, errbuflen, "SIOCGIFCONF: %s", + strerror(errno)); + (void)close(fd); + return (-1); + } + ifrp = ibuf; + ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); + + al = ifaddrlist; + mp = NULL; + nipaddr = 0; + for (; ifrp < ifend; ifrp = ifnext) { +#ifdef HAVE_SOCKADDR_SA_LEN + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + ifnext = ifrp + 1; + else + ifnext = (struct ifreq *)((char *)ifrp + n); + if (ifrp->ifr_addr.sa_family != AF_INET) + continue; +#else + ifnext = ifrp + 1; +#endif + /* + * Need a template to preserve address info that is + * used below to locate the next entry. (Otherwise, + * SIOCGIFFLAGS stomps over it because the requests + * are returned in a union.) + */ + strlcpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { + if (errno == ENXIO) + continue; + (void)snprintf(errbuf, errbuflen, "SIOCGIFFLAGS: %.*s: %s", + (int)sizeof(ifr.ifr_name), ifr.ifr_name, + strerror(errno)); + (void)close(fd); + return (-1); + } + + /* Must be up */ + if ((ifr.ifr_flags & IFF_UP) == 0) + continue; + + + (void)strlcpy(device, ifr.ifr_name, sizeof(device)); +#ifdef sun + /* Ignore sun virtual interfaces */ + if (strchr(device, ':') != NULL) + continue; +#endif + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { + (void)snprintf(errbuf, errbuflen, "SIOCGIFADDR: %s: %s", + device, strerror(errno)); + (void)close(fd); + return (-1); + } + + if (nipaddr >= MAX_IPADDR) { + (void)snprintf(errbuf, errbuflen, "Too many interfaces (%d)", + (int)MAX_IPADDR); + (void)close(fd); + return (-1); + } + sin = (struct sockaddr_in *)&ifr.ifr_addr; + al->addr = sin->sin_addr.s_addr; + al->device = strdup(device); + ++al; + ++nipaddr; + } + (void)close(fd); + + *ipaddrp = ifaddrlist; + return (nipaddr); +} diff --git a/network_cmds/traceroute.tproj/ifaddrlist.h b/network_cmds/traceroute.tproj/ifaddrlist.h new file mode 100644 index 0000000..53d91d9 --- /dev/null +++ b/network_cmds/traceroute.tproj/ifaddrlist.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: /Volumes/george/fs-svn/network_cmds/traceroute.tproj/ifaddrlist.h,v 1.2 2004/08/08 00:27:54 lindak Exp $ (LBL) + */ + +struct ifaddrlist { + u_int32_t addr; + char *device; +}; + +int ifaddrlist(struct ifaddrlist **, char *, size_t ); diff --git a/network_cmds/traceroute.tproj/mean.awk b/network_cmds/traceroute.tproj/mean.awk new file mode 100644 index 0000000..e97a56f --- /dev/null +++ b/network_cmds/traceroute.tproj/mean.awk @@ -0,0 +1,50 @@ +#!/bin/awk -f +# +# Copyright (c) 1990, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Van Jacobson. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mean.awk 8.1 (Berkeley) 6/6/93 +# +/^ *[0-9]/ { + # print out the average time to each hop along a route. + tottime = 0; n = 0; + for (f = 5; f <= NF; ++f) { + if ($f == "ms") { + tottime += $(f - 1) + ++n + } + } + if (n > 0) + print $1, tottime/n, median +} diff --git a/network_cmds/traceroute.tproj/median.awk b/network_cmds/traceroute.tproj/median.awk new file mode 100644 index 0000000..1a8d81d --- /dev/null +++ b/network_cmds/traceroute.tproj/median.awk @@ -0,0 +1,67 @@ +#!/bin/awk -f +# +# Copyright (c) 1990, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Van Jacobson. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)median.awk 8.1 (Berkeley) 6/6/93 +# +/^ *[0-9]/ { + # print out the median time to each hop along a route. + tottime = 0; n = 0; + for (f = 5; f <= NF; ++f) { + if ($f == "ms") { + ++n + time[n] = $(f - 1) + } + } + if (n > 0) { + # insertion sort the times to find the median + for (i = 2; i <= n; ++i) { + v = time[i]; j = i - 1; + while (time[j] > v) { + time[j+1] = time[j]; + j = j - 1; + if (j < 0) + break; + } + time[j+1] = v; + } + if (n > 1 && (n % 2) == 0) + median = (time[n/2] + time[(n/2) + 1]) / 2 + else + median = time[(n+1)/2] + + print $1, median + } +} diff --git a/network_cmds/traceroute.tproj/traceroute.8 b/network_cmds/traceroute.tproj/traceroute.8 new file mode 100644 index 0000000..c726d7a --- /dev/null +++ b/network_cmds/traceroute.tproj/traceroute.8 @@ -0,0 +1,439 @@ +.\" Copyright (c) 1989, 1995, 1996, 1997, 1999, 2000 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms are permitted +.\" provided that the above copyright notice and this paragraph are +.\" duplicated in all such forms and that any documentation, +.\" advertising materials, and other materials related to such +.\" distribution and use acknowledge that the software was developed +.\" by the University of California, Berkeley. The name of the +.\" University may not be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.Dd May 29, 2008 +.Dt TRACEROUTE 8 +.Os BSD 4.3 +.Sh NAME +.Nm traceroute +.Nd print the route packets take to network host +.Sh SYNOPSIS +.Nm traceroute +.Op Fl adeFISdNnrvx +.Op Fl A Ar as_server +.Op Fl f Ar first_ttl +.Op Fl g Ar gateway +.Op Fl i Ar iface +.Op Fl M Ar first_ttl +.Op Fl m Ar max_ttl +.Op Fl P Ar proto +.Op Fl p Ar port +.Op Fl q Ar nqueries +.Op Fl s Ar src_addr +.Op Fl t Ar tos +.Op Fl w Ar waittime +.Op Fl z Ar pausemsecs +.Ar host +.Op Ar packetsize +.Sh DESCRIPTION +The Internet is a large and complex aggregation of +network hardware, connected together by gateways. +Tracking the route one's packets follow (or finding the miscreant +gateway that's discarding your packets) can be difficult. +.Nm +utilizes the IP protocol `time to live' field and attempts to elicit an +.Tn ICMP +.Dv TIME_EXCEEDED +response from each gateway along the path to some +host. +.Pp +The only mandatory parameter is the destination host name or IP number. +The default probe datagram length is 40 bytes, but this may be increased +by specifying a packet size (in bytes) after the destination host +name. +.Pp +Other options are: +.Bl -tag -width Ds +.It Fl a +Turn on AS# lookups for each hop encountered. +.It Fl A Ar as_server +Turn on AS# lookups and use the given server instead of the +default. +.It Fl d +Enable socket level debugging. +.It Fl D +When an ICMP response to our probe datagram is received, +print the differences between the transmitted packet and +the packet quoted by the ICMP response. +A key showing the location of fields within the transmitted packet is printed, +followed by the original packet in hex, +followed by the quoted packet in hex. +Bytes that are unchanged in the quoted packet are shown as underscores. +Note, +the IP checksum and the TTL of the quoted packet are not expected to match. +By default, only one probe per hop is sent with this option. +.It Fl e +Firewall evasion mode. +Use fixed destination ports for UDP and TCP probes. +The destination port does NOT increment with each packet sent. +.It Fl f Ar first_ttl +Set the initial time-to-live used in the first outgoing probe packet. +.It Fl F +Set the "don't fragment" bit. +.It Fl g Ar gateway +Specify a loose source route gateway (8 maximum). +.It Fl i Ar iface +Specify a network interface to obtain the source IP address for +outgoing probe packets. This is normally only useful on a multi-homed +host. (See the +.Fl s +flag for another way to do this.) +.It Fl I +Use +.Tn ICMP +ECHO instead of +.Tn UDP +datagrams. (A synonym for "-P icmp"). +.It Fl M Ar first_ttl +Set the initial time-to-live value used in outgoing probe packets. +The default is 1, i.e., start with the first hop. +.It Fl m Ar max_ttl +Set the max time-to-live (max number of hops) used in outgoing probe +packets. The default is +.Em net.inet.ip.ttl +hops (the same default used for +.Tn TCP +connections). +.It Fl n +Print hop addresses numerically rather than symbolically and numerically +(saves a nameserver address-to-name lookup for each gateway found on the +path). +.It Fl P Ar proto +Send packets of specified IP protocol. The currently supported protocols +are: +.Tn UDP +, +.Tn TCP +, +.Tn GRE +and +.Tn ICMP +Other protocols may also be specified (either by name or by number), though +.Nm +does not implement any special knowledge of their packet formats. This +option is useful for determining which router along a path may be +blocking packets based on IP protocol number. But see BUGS below. +.It Fl p Ar port +Protocol specific. For +.Tn UDP +and +.Tn TCP, +sets the base +.Ar port +number used in probes (default is 33434). +.Nm +hopes that nothing is listening on +.Tn UDP +ports +.Em base +to +.Em base+nhops-1 +at the destination host (so an +.Tn ICMP +.Dv PORT_UNREACHABLE +message will +be returned to terminate the route tracing). If something is +listening on a port in the default range, this option can be used +to pick an unused port range. +.It Fl q Ar nqueries +Set the number of probes per ``ttl'' to +.Ar nqueries +(default is three probes). +.It Fl r +Bypass the normal routing tables and send directly to a host on an attached +network. +If the host is not on a directly-attached network, +an error is returned. +This option can be used to ping a local host through an interface +that has no route through it (e.g., after the interface was dropped by +.Xr routed 8 ) . +.It Fl s Ar src_addr +Use the following IP address +(which must be given as an IP number, not +a hostname) as the source address in outgoing probe packets. On +hosts with more than one IP address, this option can be used to +force the source address to be something other than the IP address +of the interface the probe packet is sent on. If the IP address +is not one of this machine's interface addresses, an error is +returned and nothing is sent. +(See the +.Fl i +flag for another way to do this.) +.It Fl S +Print a summary of how many probes were not answered for each hop. +.It Fl t Ar tos +Set the +.Em type-of-service +in probe packets to the following value (default zero). The value must be +a decimal integer in the range 0 to 255. This option can be used to +see if different types-of-service result in different paths. (If you +are not running a +.Bx 4.4 +or later system, this may be academic since the normal network +services like telnet and ftp don't let you control the +.Dv TOS ) . +Not all values of +.Dv TOS +are legal or +meaningful \- see the IP spec for definitions. Useful values are +probably +.Ql \-t 16 +(low delay) and +.Ql \-t 8 +(high throughput). +.It Fl v +Verbose output. Received +.Tn ICMP +packets other than +.Dv TIME_EXCEEDED +and +.Dv UNREACHABLE Ns s +are listed. +.It Fl w +Set the time (in seconds) to wait for a response to a probe (default 5 sec.). +.It Fl x +Toggle IP checksums. Normally, this prevents +.Nm +from calculating +IP checksums. In some cases, the operating system can overwrite parts of +the outgoing packet but not recalculate the checksum (so in some cases +the default is to not calculate checksums and using +.Fl x +causes them to be calculated). Note that checksums are usually required +for the last hop when using +.Tn ICMP +ECHO probes ( +.Fl I +). So they are always calculated when using ICMP. +.It Fl z Ar pausemsecs +Set the time (in milliseconds) to pause between probes (default 0). +Some systems such as Solaris and routers such as Ciscos rate limit +ICMP messages. A good value to use with this this is 500 (e.g. 1/2 second). +.El +.Pp +This program attempts to trace the route an IP packet would follow to some +internet host by launching +.Tn UDP +probe +packets with a small ttl (time to live) then listening for an +.Tn ICMP +"time exceeded" reply from a gateway. We start our probes +with a ttl of one and increase by one until we get an +.Tn ICMP +"port unreachable" +(which means we got to "host") or hit a max (which +defaults to +.Em net.inet.ip.ttl +hops & can be changed with the +.Fl m +flag). Three +probes (changed with +.Fl q +flag) are sent at each ttl setting and a +line is printed showing the ttl, address of the gateway and +round trip time of each probe. If the probe answers come from +different gateways, the address of each responding system will +be printed. If there is no response within a 5 sec. timeout +interval (changed with the +.Fl w +flag), a "*" is printed for that +probe. +.Pp +We don't want the destination +host to process the +.Tn UDP +probe packets so the destination port is set to an +unlikely value (if some clod on the destination is using that +value, it can be changed with the +.Fl p +flag). +.Pp +A sample use and output might be: +.Bd -literal +[yak 71]% traceroute nis.nsf.net. +traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 38 byte packet +1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms +2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms +3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms +4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms +5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms +6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms +7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms +8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms +9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms +10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms +11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + +.Ed +Note that lines 2 & 3 are the same. This is due to a buggy +kernel on the 2nd hop system \- lbl-csam.arpa \- that forwards +packets with a zero ttl (a bug in the distributed version +of 4.3 +.Tn BSD ) . +Note that you have to guess what path +the packets are taking cross-country since the +.Tn NSFNet +(129.140) +doesn't supply address-to-name translations for its +.Tn NSS Ns es . +.Pp +A more interesting example is: +.Bd -literal +[yak 72]% traceroute allspice.lcs.mit.edu. +traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max +1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms +2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms +3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms +4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms +5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms +6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms +7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms +8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms +9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms +10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms +11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms +12 * * * +13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms +14 * * * +15 * * * +16 * * * +17 * * * +18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + +.Ed +Note that the gateways 12, 14, 15, 16 & 17 hops away +either don't send +.Tn ICMP +"time exceeded" messages or send them +with a ttl too small to reach us. 14 \- 17 are running the +.Tn MIT +C Gateway code that doesn't send "time exceeded"s. God +only knows what's going on with 12. +.Pp +The silent gateway 12 in the above may be the result of a bug in +the 4.[23] +.Tn BSD +network code (and its derivatives): 4.x (x <= 3) +sends an unreachable message using whatever ttl remains in the +original datagram. Since, for gateways, the remaining ttl is +zero, the +.Tn ICMP +"time exceeded" is guaranteed to not make it back +to us. The behavior of this bug is slightly more interesting +when it appears on the destination system: +.Bd -literal +1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms +2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms +3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms +4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms +5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms +6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms +7 * * * +8 * * * +9 * * * +10 * * * +11 * * * +12 * * * +13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + +.Ed +Notice that there are 12 "gateways" (13 is the final +destination) and exactly the last half of them are "missing". +What's really happening is that rip (a Sun-3 running Sun OS3.5) +is using the ttl from our arriving datagram as the ttl in its +.Tn ICMP +reply. So, the reply will time out on the return path +(with no notice sent to anyone since +.Tn ICMP's +aren't sent for +.Tn ICMP's ) +until we probe with a ttl that's at least twice the path +length. I.e., rip is really only 7 hops away. A reply that +returns with a ttl of 1 is a clue this problem exists. +.Nm +prints a "!" after the time if the ttl is <= 1. +Since vendors ship a lot of obsolete +.Pf ( Tn DEC Ns \'s +Ultrix, Sun 3.x) or +non-standard +.Pq Tn HPUX +software, expect to see this problem +frequently and/or take care picking the target host of your +probes. +.Pp +Other possible annotations after the time are +.Sy !H , +.Sy !N , +or +.Sy !P +(host, network or protocol unreachable), +.Sy !S +(source route failed), +.B !F\-<pmtu> +(fragmentation needed \- the RFC1191 Path MTU Discovery value is displayed), +.Sy !U +or +.Sy !W +(destination network/host unknown), +.Sy !I +(source host is isolated), +.Sy !A +(communication with destination network administratively prohibited), +.Sy !Z +(communication with destination host administratively prohibited), +.Sy !Q +(for this ToS the destination network is unreachable), +.Sy !T +(for this ToS the destination host is unreachable), +.Sy !X +(communication administratively prohibited), +.Sy !V +(host precedence violation), +.Sy !C +(precedence cutoff in effect), or +.Sy !<num> +(ICMP unreachable code <num>). +These are defined by RFC1812 (which supersedes RFC1716). +If almost all the probes result in some kind of unreachable, +.Nm +will give up and exit. +.Pp +This program is intended for use in network testing, measurement +and management. +It should be used primarily for manual fault isolation. +Because of the load it could impose on the network, it is unwise to use +.Nm +during normal operations or from automated scripts. +.Sh AUTHOR +Implemented by Van Jacobson from a suggestion by Steve Deering. Debugged +by a cast of thousands with particularly cogent suggestions or fixes from +C. Philip Wood, Tim Seaver and Ken Adelman. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ping 8 , +.Xr traceroute6 8 +.Sh BUGS +When using protocols other than UDP, functionality is reduced. +In particular, the last packet will often appear to be lost, because +even though it reaches the destination host, there's no way to know +that because no ICMP message is sent back. +In the TCP case, +.Nm +should listen for a RST from the destination host (or an intermediate +router that's filtering packets), but this is not implemented yet. +.Pp +The AS number capability reports information that may sometimes be +inaccurate due to discrepancies between the contents of the +routing database server and the current state of the Internet. diff --git a/network_cmds/traceroute.tproj/traceroute.c b/network_cmds/traceroute.tproj/traceroute.c new file mode 100644 index 0000000..a411d0a --- /dev/null +++ b/network_cmds/traceroute.tproj/traceroute.c @@ -0,0 +1,1822 @@ +/* + * Copyright (c) 2004-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static const char copyright[] = + "@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000\n\ +The Regents of the University of California. All rights reserved.\n"; +#endif + +/* + * traceroute host - trace the route ip packets follow going to "host". + * + * Attempt to trace the route an ip packet would follow to some + * internet host. We find out intermediate hops by launching probe + * packets with a small ttl (time to live) then listening for an + * icmp "time exceeded" reply from a gateway. We start our probes + * with a ttl of one and increase by one until we get an icmp "port + * unreachable" (which means we got to "host") or hit a max (which + * defaults to net.inet.ip.ttl hops & can be changed with the -m flag). + * Three probes (change with -q flag) are sent at each ttl setting and + * a line is printed showing the ttl, address of the gateway and + * round trip time of each probe. If the probe answers come from + * different gateways, the address of each responding system will + * be printed. If there is no response within a 5 sec. timeout + * interval (changed with the -w flag), a "*" is printed for that + * probe. + * + * Probe packets are UDP format. We don't want the destination + * host to process them so the destination port is set to an + * unlikely value (if some clod on the destination is using that + * value, it can be changed with the -p flag). + * + * A sample use might be: + * + * [yak 71]% traceroute nis.nsf.net. + * traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 56 byte packet + * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms + * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms + * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms + * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + * + * Note that lines 2 & 3 are the same. This is due to a buggy + * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards + * packets with a zero ttl. + * + * A more interesting example is: + * + * [yak 72]% traceroute allspice.lcs.mit.edu. + * traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms + * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms + * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms + * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms + * 12 * * * + * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms + * 14 * * * + * 15 * * * + * 16 * * * + * 17 * * * + * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + * + * (I start to see why I'm having so much trouble with mail to + * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away + * either don't send ICMP "time exceeded" messages or send them + * with a ttl too small to reach us. 14 - 17 are running the + * MIT C Gateway code that doesn't send "time exceeded"s. God + * only knows what's going on with 12. + * + * The silent gateway 12 in the above may be the result of a bug in + * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) + * sends an unreachable message using whatever ttl remains in the + * original datagram. Since, for gateways, the remaining ttl is + * zero, the icmp "time exceeded" is guaranteed to not make it back + * to us. The behavior of this bug is slightly more interesting + * when it appears on the destination system: + * + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms + * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms + * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms + * 7 * * * + * 8 * * * + * 9 * * * + * 10 * * * + * 11 * * * + * 12 * * * + * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + * + * Notice that there are 12 "gateways" (13 is the final + * destination) and exactly the last half of them are "missing". + * What's really happening is that rip (a Sun-3 running Sun OS3.5) + * is using the ttl from our arriving datagram as the ttl in its + * icmp reply. So, the reply will time out on the return path + * (with no notice sent to anyone since icmp's aren't sent for + * icmp's) until we probe with a ttl that's at least twice the path + * length. I.e., rip is really only 7 hops away. A reply that + * returns with a ttl of 1 is a clue this problem exists. + * Traceroute prints a "!" after the time if the ttl is <= 1. + * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or + * non-standard (HPUX) software, expect to see this problem + * frequently and/or take care picking the target host of your + * probes. + * + * Other possible annotations after the time are !H, !N, !P (got a host, + * network or protocol unreachable, respectively), !S or !F (source + * route failed or fragmentation needed -- neither of these should + * ever occur and the associated gateway is busted if you see one). If + * almost all the probes result in some kind of unreachable, traceroute + * will give up and exit. + * + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * This program requires a kernel mod that does not appear in any + * system available from Berkeley: A raw ip socket using proto + * IPPROTO_RAW must interpret the data sent as an ip datagram (as + * opposed to data to be wrapped in a ip datagram). See the README + * file that came with the source to this program for a description + * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may + * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE + * MODIFIED TO RUN THIS PROGRAM. + * + * The udp port usage may appear bizarre (well, ok, it is bizarre). + * The problem is that an icmp message only contains 8 bytes of + * data from the original datagram. 8 bytes is the size of a udp + * header so, if we want to associate replies with the original + * datagram, the necessary information must be encoded into the + * udp header (the ip id could be used but there's no way to + * interlock with the kernel's assignment of ip id's and, anyway, + * it would have taken a lot more kernel hacking to allow this + * code to set the ip id). So, to allow two or more users to + * use traceroute simultaneously, we use this task's pid as the + * source port (the high bit is set to move the port number out + * of the "likely" range). To keep track of which probe is being + * replied to (so times and/or hop counts don't get confused by a + * reply that was delayed in transit), we increment the destination + * port number before each probe. + * + * Don't use this as a coding example. I was trying to find a + * routing problem and this code sort-of popped out after 48 hours + * without sleep. I was amazed it ever compiled, much less ran. + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <sys/socket.h> +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif +#include <sys/time.h> + +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/ip_icmp.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> + +#include <arpa/inet.h> + +#ifdef IPSEC +#include <net/route.h> +#include <netinet6/ipsec.h> /* XXX */ +#endif /* IPSEC */ + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <memory.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +/* rfc1716 */ +#ifndef ICMP_UNREACH_FILTER_PROHIB +#define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohibited filter */ +#endif +#ifndef ICMP_UNREACH_HOST_PRECEDENCE +#define ICMP_UNREACH_HOST_PRECEDENCE 14 /* host precedence violation */ +#endif +#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF +#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 /* precedence cutoff */ +#endif + +#include "findsaddr.h" +#include "ifaddrlist.h" +#include "as.h" +#include "traceroute.h" + +/* Maximum number of gateways (include room for one noop) */ +#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t))) + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#define Fprintf (void)fprintf +#define Printf (void)printf + +/* What a GRE packet header looks like */ +struct grehdr { + u_int16_t flags; + u_int16_t proto; + u_int16_t length; /* PPTP version of these fields */ + u_int16_t callId; +}; +#ifndef IPPROTO_GRE +#define IPPROTO_GRE 47 +#endif + +/* For GRE, we prepare what looks like a PPTP packet */ +#define GRE_PPTP_PROTO 0x880b + +/* Host name and address list */ +struct hostinfo { + char *name; + int n; + u_int32_t *addrs; +}; + +/* Data section of the probe packet */ +struct outdata { + u_char seq; /* sequence number of this packet */ + u_char ttl; /* ttl packet left with */ + struct timeval tv; /* time packet left */ +}; + +#ifndef HAVE_ICMP_NEXTMTU +/* Path MTU Discovery (RFC1191) */ +struct my_pmtu { + u_short ipm_void; + u_short ipm_nextmtu; +}; +#endif + +u_char packet[512]; /* last inbound (icmp) packet */ + +struct ip *outip; /* last output ip packet */ +u_char *outp; /* last output inner protocol packet */ + +struct ip *hip = NULL; /* Quoted IP header */ +int hiplen = 0; + +/* loose source route gateway list (including room for final destination) */ +u_int32_t gwlist[NGATEWAYS + 1]; + +int s; /* receive (icmp) socket file descriptor */ +int sndsock; /* send (udp) socket file descriptor */ + +struct sockaddr whereto; /* Who to try to reach */ +struct sockaddr wherefrom; /* Who we are */ +int packlen; /* total length of packet */ +int protlen; /* length of protocol part of packet */ +int minpacket; /* min ip packet size */ +int maxpacket = 32 * 1024; /* max ip packet size */ +int pmtu; /* Path MTU Discovery (RFC1191) */ +u_int pausemsecs; + +char *prog; +char *source; +char *hostname; +char *device; +static const char devnull[] = "/dev/null"; + +int nprobes = -1; +int max_ttl; +int first_ttl = 1; +u_short ident; +u_short port; /* protocol specific base "port" */ + +int options; /* socket options */ +int verbose; +int waittime = 5; /* time to wait for response (in seconds) */ +int nflag; /* print addresses numerically */ +int as_path; /* print as numbers for each hop */ +char *as_server = NULL; +void *asn; +#ifdef CANT_HACK_IPCKSUM +int doipcksum = 0; /* don't calculate ip checksums by default */ +#else +int doipcksum = 1; /* calculate ip checksums by default */ +#endif +int optlen; /* length of ip options */ +int fixedPort = 0; /* Use fixed destination port for TCP and UDP */ +int printdiff = 0; /* Print the difference between sent and quoted */ + +extern int optind; +extern int opterr; +extern char *optarg; + +/* Forwards */ +double deltaT(struct timeval *, struct timeval *); +void freehostinfo(struct hostinfo *); +void getaddr(u_int32_t *, char *); +struct hostinfo *gethostinfo(char *); +u_short in_cksum(u_short *, int); +char *inetname(struct in_addr); +int main(int, char **); +u_short p_cksum(struct ip *, u_short *, int); +int packet_ok(u_char *, int, struct sockaddr_in *, int); +char *pr_type(u_char); +void print(u_char *, int, struct sockaddr_in *); +#ifdef IPSEC +int setpolicy __P((int so, char *policy)); +#endif +void send_probe(int, int); +struct outproto *setproto(char *); +int str2val(const char *, const char *, int, int); +void tvsub(struct timeval *, struct timeval *); +void usage(void); +int wait_for_reply(int, struct sockaddr_in *, const struct timeval *); +void pkt_compare(const u_char *, int, const u_char *, int); +#ifndef HAVE_USLEEP +int usleep(u_int); +#endif + +void udp_prep(struct outdata *); +int udp_check(const u_char *, int); +void tcp_prep(struct outdata *); +int tcp_check(const u_char *, int); +void gre_prep(struct outdata *); +int gre_check(const u_char *, int); +void gen_prep(struct outdata *); +int gen_check(const u_char *, int); +void icmp_prep(struct outdata *); +int icmp_check(const u_char *, int); + +/* Descriptor structure for each outgoing protocol we support */ +struct outproto { + char *name; /* name of protocol */ + const char *key; /* An ascii key for the bytes of the header */ + u_char num; /* IP protocol number */ + u_short hdrlen; /* max size of protocol header */ + u_short port; /* default base protocol-specific "port" */ + void (*prepare)(struct outdata *); + /* finish preparing an outgoing packet */ + int (*check)(const u_char *, int); + /* check an incoming packet */ +}; + +/* List of supported protocols. The first one is the default. The last + one is the handler for generic protocols not explicitly listed. */ +struct outproto protos[] = { + { + "udp", + "spt dpt len sum", + IPPROTO_UDP, + sizeof(struct udphdr), + 32768 + 666, + udp_prep, + udp_check + }, + { + "tcp", + "spt dpt seq ack xxflwin sum urp", + IPPROTO_TCP, + sizeof(struct tcphdr), + 32768 + 666, + tcp_prep, + tcp_check + }, + { + "gre", + "flg pro len clid", + IPPROTO_GRE, + sizeof(struct grehdr), + GRE_PPTP_PROTO, + gre_prep, + gre_check + }, + { + "icmp", + "typ cod sum ", + IPPROTO_ICMP, + sizeof(struct icmp), + 0, + icmp_prep, + icmp_check + }, + { + NULL, + NULL, + 0, + 2 * sizeof(u_short), + 0, + gen_prep, + gen_check + }, +}; +struct outproto *proto = &protos[0]; + +const char *ip_hdr_key = "vhtslen id off tlprsum srcip dstip opts"; + +int +main(int argc, char **argv) +{ + register int op, code, n; + register char *cp; + register const char *err; + register u_int32_t *ap; + register struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom; + register struct sockaddr_in *to = (struct sockaddr_in *)&whereto; + register struct hostinfo *hi; + int on = 1; + register struct protoent *pe; + register int ttl, probe, i; + register int seq = 0; + int tos = 0, settos = 0; + register int lsrr = 0; + register u_short off = 0; + struct ifaddrlist *al; + char errbuf[132]; + int requestPort = -1; + int sump = 0; + int sockerrno = 0; + + if (argv[0] == NULL) + prog = "traceroute"; + else if ((cp = strrchr(argv[0], '/')) != NULL) + prog = cp + 1; + else + prog = argv[0]; + + /* Insure the socket fds won't be 0, 1 or 2 */ + if (open(devnull, O_RDONLY) < 0 || + open(devnull, O_RDONLY) < 0 || + open(devnull, O_RDONLY) < 0) { + Fprintf(stderr, "%s: open \"%s\": %s\n", + prog, devnull, strerror(errno)); + exit(1); + } + /* + * Do the setuid-required stuff first, then lose priveleges ASAP. + * Do error checking for these two calls where they appeared in + * the original code. + */ + cp = "icmp"; + pe = getprotobyname(cp); + if (pe) { + if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) + sockerrno = errno; + else if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + sockerrno = errno; + } + + setuid(getuid()); + +#ifdef IPCTL_DEFTTL + { + int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL }; + size_t sz = sizeof(max_ttl); + + if (sysctl(mib, 4, &max_ttl, &sz, NULL, 0) == -1) { + perror("sysctl(net.inet.ip.ttl)"); + exit(1); + } + } +#else + max_ttl = 30; +#endif + + opterr = 0; + while ((op = getopt(argc, argv, "aA:edDFInrSvxf:g:i:M:m:P:p:q:s:t:w:z:")) != EOF) + switch (op) { + case 'a': + as_path = 1; + break; + + case 'A': + as_path = 1; + as_server = optarg; + break; + + case 'd': + options |= SO_DEBUG; + break; + + case 'D': + printdiff = 1; + break; + + case 'e': + fixedPort = 1; + break; + + case 'f': + case 'M': /* FreeBSD compat. */ + first_ttl = str2val(optarg, "first ttl", 1, 255); + break; + + case 'F': + off = IP_DF; + break; + + case 'g': + if (lsrr >= NGATEWAYS) { + Fprintf(stderr, + "%s: No more than %d gateways\n", + prog, NGATEWAYS); + exit(1); + } + getaddr(gwlist + lsrr, optarg); + ++lsrr; + break; + + case 'i': + device = optarg; + break; + + case 'I': + proto = setproto("icmp"); + break; + + case 'm': + max_ttl = str2val(optarg, "max ttl", 1, 255); + break; + + case 'n': + ++nflag; + break; + + case 'P': + proto = setproto(optarg); + break; + + case 'p': + requestPort = (u_short)str2val(optarg, "port", + 1, (1 << 16) - 1); + break; + + case 'q': + nprobes = str2val(optarg, "nprobes", 1, -1); + break; + + case 'r': + options |= SO_DONTROUTE; + break; + + case 's': + /* + * set the ip source address of the outbound + * probe (e.g., on a multi-homed host). + */ + source = optarg; + break; + + case 'S': + sump = 1; + break; + + case 't': + tos = str2val(optarg, "tos", 0, 255); + ++settos; + break; + + case 'v': + ++verbose; + break; + + case 'x': + doipcksum = (doipcksum == 0); + break; + + case 'w': + waittime = str2val(optarg, "wait time", + 1, 24 * 60 * 60); + break; + + case 'z': + pausemsecs = str2val(optarg, "pause msecs", + 0, 60 * 60 * 1000); + break; + + default: + usage(); + } + + /* Set requested port, if any, else default for this protocol */ + port = (requestPort != -1) ? requestPort : proto->port; + + if (nprobes == -1) + nprobes = printdiff ? 1 : 3; + + if (first_ttl > max_ttl) { + Fprintf(stderr, + "%s: first ttl (%d) may not be greater than max ttl (%d)\n", + prog, first_ttl, max_ttl); + exit(1); + } + + if (!doipcksum) + Fprintf(stderr, "%s: Warning: ip checksums disabled\n", prog); + + if (lsrr > 0) + optlen = (lsrr + 1) * sizeof(gwlist[0]); + minpacket = sizeof(*outip) + proto->hdrlen + sizeof(struct outdata) + optlen; + packlen = minpacket; /* minimum sized packet */ + + /* Process destination and optional packet size */ + switch (argc - optind) { + + case 2: + packlen = str2val(argv[optind + 1], + "packet length", minpacket, maxpacket); + /* Fall through */ + + case 1: + hostname = argv[optind]; + hi = gethostinfo(hostname); + setsin(to, hi->addrs[0]); + if (hi->n > 1) + Fprintf(stderr, + "%s: Warning: %s has multiple addresses; using %s\n", + prog, hostname, inet_ntoa(to->sin_addr)); + hostname = hi->name; + hi->name = NULL; + freehostinfo(hi); + break; + + default: + usage(); + } + +#ifdef HAVE_SETLINEBUF + setlinebuf (stdout); +#else + setvbuf(stdout, NULL, _IOLBF, 0); +#endif + + protlen = packlen - sizeof(*outip) - optlen; + + outip = (struct ip *)malloc((unsigned)packlen); + if (outip == NULL) { + Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno)); + exit(1); + } + memset((char *)outip, 0, packlen); + + outip->ip_v = IPVERSION; + if (settos) + outip->ip_tos = tos; +#ifdef BYTESWAP_IP_HDR + outip->ip_len = htons(packlen); + outip->ip_off = htons(off); +#else + outip->ip_len = packlen; + outip->ip_off = off; +#endif + outip->ip_p = proto->num; + outp = (u_char *)(outip + 1); +#ifdef HAVE_RAW_OPTIONS + if (lsrr > 0) { + register u_char *optlist; + + optlist = outp; + outp += optlen; + + /* final hop */ + gwlist[lsrr] = to->sin_addr.s_addr; + + outip->ip_dst.s_addr = gwlist[0]; + + /* force 4 byte alignment */ + optlist[0] = IPOPT_NOP; + /* loose source route option */ + optlist[1] = IPOPT_LSRR; + i = lsrr * sizeof(gwlist[0]); + optlist[2] = i + 3; + /* Pointer to LSRR addresses */ + optlist[3] = IPOPT_MINOFF; + memcpy(optlist + 4, gwlist + 1, i); + } else +#endif + outip->ip_dst = to->sin_addr; + + outip->ip_hl = (outp - (u_char *)outip) >> 2; + ident = (getpid() & 0xffff) | 0x8000; + + if (pe == NULL) { + Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp); + exit(1); + } + if (s < 0) { + errno = sockerrno; + Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno)); + exit(1); + } + (void) setsockopt(s, SOL_SOCKET, SO_RECV_ANYIF, (char *)&on, + sizeof(on)); + if (options & SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, + sizeof(on)); + if (options & SO_DONTROUTE) + (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on, + sizeof(on)); + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(s, "in bypass") < 0) + errx(1, "%s", ipsec_strerror()); + + if (setpolicy(s, "out bypass") < 0) + errx(1, "%s", ipsec_strerror()); +#endif /* defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) */ + + if (sndsock < 0) { + errno = sockerrno; + Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno)); + exit(1); + } + +#if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS) + if (lsrr > 0) { + u_char optlist[MAX_IPOPTLEN]; + + cp = "ip"; + if ((pe = getprotobyname(cp)) == NULL) { + Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp); + exit(1); + } + + /* final hop */ + gwlist[lsrr] = to->sin_addr.s_addr; + ++lsrr; + + /* force 4 byte alignment */ + optlist[0] = IPOPT_NOP; + /* loose source route option */ + optlist[1] = IPOPT_LSRR; + i = lsrr * sizeof(gwlist[0]); + optlist[2] = i + 3; + /* Pointer to LSRR addresses */ + optlist[3] = IPOPT_MINOFF; + memcpy(optlist + 4, gwlist, i); + + if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS, + (char *)optlist, i + sizeof(gwlist[0]))) < 0) { + Fprintf(stderr, "%s: IP_OPTIONS: %s\n", + prog, strerror(errno)); + exit(1); + } + } +#endif + +#ifdef SO_SNDBUF + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen, + sizeof(packlen)) < 0) { + Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno)); + exit(1); + } +#endif +#ifdef IP_HDRINCL + if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) < 0) { + Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno)); + exit(1); + } +#else +#ifdef IP_TOS + if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS, + (char *)&tos, sizeof(tos)) < 0) { + Fprintf(stderr, "%s: setsockopt tos %d: %s\n", + prog, tos, strerror(errno)); + exit(1); + } +#endif +#endif + if (options & SO_DEBUG) + (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on, + sizeof(on)); + if (options & SO_DONTROUTE) + (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, + sizeof(on)); + + /* Get the interface address list */ + n = ifaddrlist(&al, errbuf, sizeof(errbuf)); + if (n < 0) { + Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf); + exit(1); + } + if (n == 0) { + Fprintf(stderr, + "%s: Can't find any network interfaces\n", prog); + exit(1); + } + + /* Look for a specific device */ + if (device != NULL) { + for (i = n; i > 0; --i, ++al) + if (strcmp(device, al->device) == 0) + break; + if (i <= 0) { + Fprintf(stderr, "%s: Can't find interface %.32s\n", + prog, device); + exit(1); + } + } + + /* Determine our source address */ + if (source == NULL) { + /* + * If a device was specified, use the interface address. + * Otherwise, try to determine our source address. + */ + if (device != NULL) + setsin(from, al->addr); + else if ((err = findsaddr(to, from)) != NULL) { + Fprintf(stderr, "%s: findsaddr: %s\n", + prog, err); + exit(1); + } + } else { + hi = gethostinfo(source); + source = hi->name; + hi->name = NULL; + /* + * If the device was specified make sure it + * corresponds to the source address specified. + * Otherwise, use the first address (and warn if + * there are more than one). + */ + if (device != NULL) { + for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap) + if (*ap == al->addr) + break; + if (i <= 0) { + Fprintf(stderr, + "%s: %s is not on interface %.32s\n", + prog, source, device); + exit(1); + } + setsin(from, *ap); + } else { + setsin(from, hi->addrs[0]); + if (hi->n > 1) + Fprintf(stderr, + "%s: Warning: %s has multiple addresses; using %s\n", + prog, source, inet_ntoa(from->sin_addr)); + } + freehostinfo(hi); + } + + outip->ip_src = from->sin_addr; + + /* Check the source address (-s), if any, is valid */ + if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) { + Fprintf(stderr, "%s: bind: %s\n", + prog, strerror(errno)); + exit (1); + } + + if (as_path) { + asn = as_setup(as_server); + if (asn == NULL) { + Fprintf(stderr, "%s: as_setup failed, AS# lookups" + " disabled\n", prog); + (void)fflush(stderr); + as_path = 0; + } + } + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(sndsock, "in bypass") < 0) + errx(1, "%s", ipsec_strerror()); + + if (setpolicy(sndsock, "out bypass") < 0) + errx(1, "%s", ipsec_strerror()); +#endif /* defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) */ + + Fprintf(stderr, "%s to %s (%s)", + prog, hostname, inet_ntoa(to->sin_addr)); + if (source) + Fprintf(stderr, " from %s", source); + Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen); + (void)fflush(stderr); + + for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { + u_int32_t lastaddr = 0; + int gotlastaddr = 0; + int got_there = 0; + int unreachable = 0; + int sentfirst = 0; + int loss; + + Printf("%2d ", ttl); + for (probe = 0, loss = 0; probe < nprobes; ++probe) { + register int cc; + struct timeval t1, t2; + struct timezone tz; + register struct ip *ip; + struct outdata outdata; + + if (sentfirst && pausemsecs > 0) + usleep(pausemsecs * 1000); + /* Prepare outgoing data */ + outdata.seq = ++seq; + outdata.ttl = ttl; + + /* Avoid alignment problems by copying bytewise: */ + (void)gettimeofday(&t1, &tz); + memcpy(&outdata.tv, &t1, sizeof(outdata.tv)); + + /* Finalize and send packet */ + (*proto->prepare)(&outdata); + send_probe(seq, ttl); + ++sentfirst; + + /* Wait for a reply */ + while ((cc = wait_for_reply(s, from, &t1)) != 0) { + double T; + int precis; + + (void)gettimeofday(&t2, &tz); + i = packet_ok(packet, cc, from, seq); + /* Skip short packet */ + if (i == 0) + continue; + if (!gotlastaddr || + from->sin_addr.s_addr != lastaddr) { + if (gotlastaddr) printf("\n "); + print(packet, cc, from); + lastaddr = from->sin_addr.s_addr; + ++gotlastaddr; + } + T = deltaT(&t1, &t2); +#ifdef SANE_PRECISION + if (T >= 1000.0) + precis = 0; + else if (T >= 100.0) + precis = 1; + else if (T >= 10.0) + precis = 2; + else +#endif + precis = 3; + Printf(" %.*f ms", precis, T); + if (printdiff) { + Printf("\n"); + Printf("%*.*s%s\n", + -(outip->ip_hl << 3), + outip->ip_hl << 3, + ip_hdr_key, + proto->key); + pkt_compare((void *)outip, packlen, + (void *)hip, hiplen); + } + if (i == -2) { +#ifndef ARCHAIC + ip = (struct ip *)packet; + if (ip->ip_ttl <= 1) + Printf(" !"); +#endif + ++got_there; + break; + } + /* time exceeded in transit */ + if (i == -1) + break; + code = i - 1; + switch (code) { + + case ICMP_UNREACH_PORT: +#ifndef ARCHAIC + ip = (struct ip *)packet; + if (ip->ip_ttl <= 1) + Printf(" !"); +#endif + ++got_there; + break; + + case ICMP_UNREACH_NET: + ++unreachable; + Printf(" !N"); + break; + + case ICMP_UNREACH_HOST: + ++unreachable; + Printf(" !H"); + break; + + case ICMP_UNREACH_PROTOCOL: + ++got_there; + Printf(" !P"); + break; + + case ICMP_UNREACH_NEEDFRAG: + ++unreachable; + Printf(" !F-%d", pmtu); + break; + + case ICMP_UNREACH_SRCFAIL: + ++unreachable; + Printf(" !S"); + break; + + case ICMP_UNREACH_NET_UNKNOWN: + ++unreachable; + Printf(" !U"); + break; + + case ICMP_UNREACH_HOST_UNKNOWN: + ++unreachable; + Printf(" !W"); + break; + + case ICMP_UNREACH_ISOLATED: + ++unreachable; + Printf(" !I"); + break; + + case ICMP_UNREACH_NET_PROHIB: + ++unreachable; + Printf(" !A"); + break; + + case ICMP_UNREACH_HOST_PROHIB: + ++unreachable; + Printf(" !Z"); + break; + + case ICMP_UNREACH_TOSNET: + ++unreachable; + Printf(" !Q"); + break; + + case ICMP_UNREACH_TOSHOST: + ++unreachable; + Printf(" !T"); + break; + + case ICMP_UNREACH_FILTER_PROHIB: + ++unreachable; + Printf(" !X"); + break; + + case ICMP_UNREACH_HOST_PRECEDENCE: + ++unreachable; + Printf(" !V"); + break; + + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + ++unreachable; + Printf(" !C"); + break; + + default: + ++unreachable; + Printf(" !<%d>", code); + break; + } + break; + } + if (cc == 0) { + loss++; + Printf(" *"); + } + (void)fflush(stdout); + } + if (sump) { + Printf(" (%d%% loss)", (loss * 100) / nprobes); + } + putchar('\n'); + if (got_there || + (unreachable > 0 && unreachable >= nprobes - 1)) + break; + } + if (as_path) + as_shutdown(asn); + exit(0); +} + +int +wait_for_reply(register int sock, register struct sockaddr_in *fromp, + register const struct timeval *tp) +{ + fd_set *fdsp; + size_t nfds; + struct timeval now, wait; + struct timezone tz; + register int cc = 0; + register int error; + socklen_t fromlen = sizeof(*fromp); + + nfds = howmany(sock + 1, NFDBITS); + if ((fdsp = malloc(nfds * sizeof(fd_set))) == NULL) + err(1, "malloc"); + memset(fdsp, 0, nfds * sizeof(fd_set)); + FD_SET(sock, fdsp); + + wait.tv_sec = tp->tv_sec + waittime; + wait.tv_usec = tp->tv_usec; + (void)gettimeofday(&now, &tz); + tvsub(&wait, &now); + if (wait.tv_sec < 0) { + wait.tv_sec = 0; + wait.tv_usec = 1; + } + + error = select(sock + 1, fdsp, NULL, NULL, &wait); + if (error == -1 && errno == EINVAL) { + Fprintf(stderr, "%s: botched select() args\n", prog); + exit(1); + } + if (error > 0) + cc = recvfrom(sock, (char *)packet, sizeof(packet), 0, + (struct sockaddr *)fromp, &fromlen); + + free(fdsp); + return(cc); +} + +void +send_probe(int seq, int ttl) +{ + register int cc; + + outip->ip_ttl = ttl; + outip->ip_id = htons(ident + seq); + + /* XXX undocumented debugging hack */ + if (verbose > 1) { + register const u_short *sp; + register int nshorts, i; + + sp = (u_short *)outip; + nshorts = (u_int)packlen / sizeof(u_short); + i = 0; + Printf("[ %d bytes", packlen); + while (--nshorts >= 0) { + if ((i++ % 8) == 0) + Printf("\n\t"); + Printf(" %04x", ntohs(*sp++)); + } + if (packlen & 1) { + if ((i % 8) == 0) + Printf("\n\t"); + Printf(" %02x", *(u_char *)sp); + } + Printf("]\n"); + } + +#if !defined(IP_HDRINCL) && defined(IP_TTL) + if (setsockopt(sndsock, IPPROTO_IP, IP_TTL, + (char *)&ttl, sizeof(ttl)) < 0) { + Fprintf(stderr, "%s: setsockopt ttl %d: %s\n", + prog, ttl, strerror(errno)); + exit(1); + } +#endif + + cc = sendto(sndsock, (char *)outip, + packlen, 0, &whereto, sizeof(whereto)); + if (cc < 0 || cc != packlen) { + if (cc < 0) + Fprintf(stderr, "%s: sendto: %s\n", + prog, strerror(errno)); + Printf("%s: wrote %s %d chars, ret=%d\n", + prog, hostname, packlen, cc); + (void)fflush(stdout); + } +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +int +setpolicy(so, policy) + int so; + char *policy; +{ + char *buf; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + warnx("%s", ipsec_strerror()); + return -1; + } + (void)setsockopt(so, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)); + + free(buf); + + return 0; +} +#endif + +double +deltaT(struct timeval *t1p, struct timeval *t2p) +{ + register double dt; + + dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + + (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; + return (dt); +} + +/* + * Convert an ICMP "type" field to a printable string. + */ +char * +pr_type(register u_char t) +{ + static char *ttab[] = { + "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable", + "Source Quench", "Redirect", "ICMP 6", "ICMP 7", + "Echo", "ICMP 9", "ICMP 10", "Time Exceeded", + "Param Problem", "Timestamp", "Timestamp Reply", "Info Request", + "Info Reply" + }; + + if (t > 16) + return("OUT-OF-RANGE"); + + return(ttab[t]); +} + +int +packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from, + register int seq) +{ + register struct icmp *icp; + register u_char type, code; + register int hlen; +#ifndef ARCHAIC + register struct ip *ip; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (verbose) + Printf("packet too short (%d bytes) from %s\n", cc, + inet_ntoa(from->sin_addr)); + return (0); + } + cc -= hlen; + icp = (struct icmp *)(buf + hlen); +#else + icp = (struct icmp *)buf; +#endif + type = icp->icmp_type; + code = icp->icmp_code; + /* Path MTU Discovery (RFC1191) */ + if (code != ICMP_UNREACH_NEEDFRAG) + pmtu = 0; + else { +#ifdef HAVE_ICMP_NEXTMTU + pmtu = ntohs(icp->icmp_nextmtu); +#else + pmtu = ntohs(((struct my_pmtu *)&icp->icmp_void)->ipm_nextmtu); +#endif + } + if (type == ICMP_ECHOREPLY + && proto->num == IPPROTO_ICMP + && (*proto->check)((u_char *)icp, (u_char)seq)) + return -2; + if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || + type == ICMP_UNREACH) { + u_char *inner; + + hip = &icp->icmp_ip; + hiplen = ((u_char *)icp + cc) - (u_char *)hip; + hlen = hip->ip_hl << 2; + inner = (u_char *)((u_char *)hip + hlen); + if (hlen + 12 <= cc + && hip->ip_p == proto->num + && (*proto->check)(inner, (u_char)seq)) + return (type == ICMP_TIMXCEED ? -1 : code + 1); + } +#ifndef ARCHAIC + if (verbose) { + register int i; + u_int32_t *lp = (u_int32_t *)&icp->icmp_ip; + + Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr)); + Printf("%s: icmp type %d (%s) code %d\n", + inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code); + for (i = 4; i < cc ; i += sizeof(*lp)) + Printf("%2d: x%8.8x\n", i, *lp++); + } +#endif + return(0); +} + +void +icmp_prep(struct outdata *outdata) +{ + struct icmp *const icmpheader = (struct icmp *) outp; + + icmpheader->icmp_type = ICMP_ECHO; + icmpheader->icmp_id = htons(ident); + icmpheader->icmp_seq = htons(outdata->seq); + icmpheader->icmp_cksum = 0; + icmpheader->icmp_cksum = in_cksum((u_short *)icmpheader, protlen); + if (icmpheader->icmp_cksum == 0) + icmpheader->icmp_cksum = 0xffff; +} + +int +icmp_check(const u_char *data, int seq) +{ + struct icmp *const icmpheader = (struct icmp *) data; + + return (icmpheader->icmp_id == htons(ident) + && icmpheader->icmp_seq == htons(seq)); +} + +void +udp_prep(struct outdata *outdata) +{ + struct udphdr *const outudp = (struct udphdr *) outp; + + outudp->uh_sport = htons(ident + (fixedPort ? outdata->seq : 0)); + outudp->uh_dport = htons(port + (fixedPort ? 0 : outdata->seq)); + outudp->uh_ulen = htons((u_short)protlen); + outudp->uh_sum = 0; + if (doipcksum) { + u_short sum = p_cksum(outip, (u_short*)outudp, protlen); + outudp->uh_sum = (sum) ? sum : 0xffff; + } + + return; +} + +int +udp_check(const u_char *data, int seq) +{ + struct udphdr *const udp = (struct udphdr *) data; + + return (ntohs(udp->uh_sport) == ident + (fixedPort ? seq : 0) && + ntohs(udp->uh_dport) == port + (fixedPort ? 0 : seq)); +} + +void +tcp_prep(struct outdata *outdata) +{ + struct tcphdr *const tcp = (struct tcphdr *) outp; + + tcp->th_sport = htons(ident); + tcp->th_dport = htons(port + (fixedPort ? 0 : outdata->seq)); + tcp->th_seq = (tcp->th_sport << 16) | (tcp->th_dport + + (fixedPort ? outdata->seq : 0)); + tcp->th_ack = 0; + tcp->th_off = 5; + tcp->th_flags = TH_SYN; + tcp->th_sum = 0; + + if (doipcksum) { + u_short sum = p_cksum(outip, (u_short*)tcp, protlen); + tcp->th_sum = (sum) ? sum : 0xffff; + } +} + +int +tcp_check(const u_char *data, int seq) +{ + struct tcphdr *const tcp = (struct tcphdr *) data; + + return (ntohs(tcp->th_sport) == ident + && ntohs(tcp->th_dport) == port + (fixedPort ? 0 : seq)) + && tcp->th_seq == ((ident << 16) | (port + seq)); +} + +void +gre_prep(struct outdata *outdata) +{ + struct grehdr *const gre = (struct grehdr *) outp; + + gre->flags = htons(0x2001); + gre->proto = htons(port); + gre->length = 0; + gre->callId = htons(ident + outdata->seq); +} + +int +gre_check(const u_char *data, int seq) +{ + struct grehdr *const gre = (struct grehdr *) data; + + return(ntohs(gre->proto) == port + && ntohs(gre->callId) == ident + seq); +} + +void +gen_prep(struct outdata *outdata) +{ + u_int16_t *const ptr = (u_int16_t *) outp; + + ptr[0] = htons(ident); + ptr[1] = htons(port + outdata->seq); +} + +int +gen_check(const u_char *data, int seq) +{ + u_int16_t *const ptr = (u_int16_t *) data; + + return(ntohs(ptr[0]) == ident + && ntohs(ptr[1]) == port + seq); +} + +void +print(register u_char *buf, register int cc, register struct sockaddr_in *from) +{ + register struct ip *ip; + register int hlen; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + cc -= hlen; + + if (as_path) + Printf(" [AS%d]", as_lookup(asn, &from->sin_addr)); + + if (nflag) + Printf(" %s", inet_ntoa(from->sin_addr)); + else + Printf(" %s (%s)", inetname(from->sin_addr), + inet_ntoa(from->sin_addr)); + + if (verbose) + Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst)); +} + +/* + * Checksum routine for UDP and TCP headers. + */ +u_short +p_cksum(struct ip *ip, u_short *data, int len) +{ + static struct ipovly ipo; + u_short sumh, sumd; + u_int32_t sumt; + + ipo.ih_pr = ip->ip_p; + ipo.ih_len = htons(len); + ipo.ih_src = ip->ip_src; + ipo.ih_dst = ip->ip_dst; + + sumh = in_cksum((u_short*)&ipo, sizeof(ipo)); /* pseudo ip hdr cksum */ + sumd = in_cksum((u_short*)data, len); /* payload data cksum */ + sumt = (sumh << 16) | (sumd); + + return ~in_cksum((u_short*)&sumt, sizeof(sumt)); +} + +/* + * Checksum routine for Internet Protocol family headers (C Version) + */ +u_short +in_cksum(register u_short *addr, register int len) +{ + register int nleft = len; + register u_short *w = addr; + register u_short answer; + register int sum = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) + sum += *(u_char *)w; + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +/* + * Subtract 2 timeval structs: out = out - in. + * Out is assumed to be within about LONG_MAX seconds of in. + */ +void +tvsub(register struct timeval *out, register struct timeval *in) +{ + + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +char * +inetname(struct in_addr in) +{ + register char *cp; + register struct hostent *hp; + static int first = 1; + static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1]; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, sizeof(domain) - 1) < 0) + domain[0] = '\0'; + else { + cp = strchr(domain, '.'); + if (cp == NULL) { + hp = gethostbyname(domain); + if (hp != NULL) + cp = strchr(hp->h_name, '.'); + } + if (cp == NULL) + domain[0] = '\0'; + else { + ++cp; + memmove(domain, cp, strlen(cp) + 1); + } + } + } + if (!nflag && in.s_addr != INADDR_ANY) { + hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET); + if (hp != NULL) { + if ((cp = strchr(hp->h_name, '.')) != NULL && + strcmp(cp + 1, domain) == 0) + *cp = '\0'; + (void)strlcpy(line, hp->h_name, sizeof(line)); + return (line); + } + } + return (inet_ntoa(in)); +} + +struct hostinfo * +gethostinfo(register char *hostname) +{ + register int n; + register struct hostent *hp; + register struct hostinfo *hi; + register char **p; + register u_int32_t addr, *ap; + + if (strlen(hostname) > 64) { + Fprintf(stderr, "%s: hostname \"%.32s...\" is too long\n", + prog, hostname); + exit(1); + } + hi = calloc(1, sizeof(*hi)); + if (hi == NULL) { + Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno)); + exit(1); + } + addr = inet_addr(hostname); + if ((int32_t)addr != -1) { + hi->name = strdup(hostname); + hi->n = 1; + hi->addrs = calloc(1, sizeof(hi->addrs[0])); + if (hi->addrs == NULL) { + Fprintf(stderr, "%s: calloc %s\n", + prog, strerror(errno)); + exit(1); + } + hi->addrs[0] = addr; + return (hi); + } + + hp = gethostbyname(hostname); + if (hp == NULL) { + Fprintf(stderr, "%s: unknown host %s\n", prog, hostname); + exit(1); + } + if (hp->h_addrtype != AF_INET || hp->h_length != 4) { + Fprintf(stderr, "%s: bad host %s\n", prog, hostname); + exit(1); + } + hi->name = strdup(hp->h_name); + for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p) + continue; + hi->n = n; + hi->addrs = calloc(n, sizeof(hi->addrs[0])); + if (hi->addrs == NULL) { + Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno)); + exit(1); + } + for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p) + memcpy(ap, *p, sizeof(*ap)); + return (hi); +} + +void +freehostinfo(register struct hostinfo *hi) +{ + if (hi->name != NULL) { + free(hi->name); + hi->name = NULL; + } + free((char *)hi->addrs); + free((char *)hi); +} + +void +getaddr(register u_int32_t *ap, register char *hostname) +{ + register struct hostinfo *hi; + + hi = gethostinfo(hostname); + *ap = hi->addrs[0]; + freehostinfo(hi); +} + +void +setsin(register struct sockaddr_in *sin, register u_int32_t addr) +{ + + memset(sin, 0, sizeof(*sin)); +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = addr; +} + +/* String to value with optional min and max. Handles decimal and hex. */ +int +str2val(register const char *str, register const char *what, + register int mi, register int ma) +{ + register const char *cp; + register int val; + char *ep; + + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + val = (int)strtol(cp, &ep, 16); + } else + val = (int)strtol(str, &ep, 10); + if (*ep != '\0') { + Fprintf(stderr, "%s: \"%s\" bad value for %s \n", + prog, str, what); + exit(1); + } + if (val < mi && mi >= 0) { + if (mi == 0) + Fprintf(stderr, "%s: %s must be >= %d\n", + prog, what, mi); + else + Fprintf(stderr, "%s: %s must be > %d\n", + prog, what, mi - 1); + exit(1); + } + if (val > ma && ma >= 0) { + Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma); + exit(1); + } + return (val); +} + +struct outproto * +setproto(char *pname) +{ + struct outproto *proto; + int i; + + for (i = 0; protos[i].name != NULL; i++) { + if (strcasecmp(protos[i].name, pname) == 0) { + break; + } + } + proto = &protos[i]; + if (proto->name == NULL) { /* generic handler */ + struct protoent *pe; + u_int32_t pnum; + + /* Determine the IP protocol number */ + if ((pe = getprotobyname(pname)) != NULL) + pnum = pe->p_proto; + else + pnum = str2val(optarg, "proto number", 1, 255); + proto->num = pnum; + } + return proto; +} + +void +pkt_compare(const u_char *a, int la, const u_char *b, int lb) { + int l; + int i; + + for (i = 0; i < la; i++) + Printf("%02x", (unsigned int)a[i]); + Printf("\n"); + l = (la <= lb) ? la : lb; + for (i = 0; i < l; i++) + if (a[i] == b[i]) + Printf("__"); + else + Printf("%02x", (unsigned int)b[i]); + for (; i < lb; i++) + Printf("%02x", (unsigned int)b[i]); + Printf("\n"); +} + + +void +usage(void) +{ + extern char version[]; + + Fprintf(stderr, "Version %s\n", version); + Fprintf(stderr, + "Usage: %s [-adDeFInrSvx] [-A as_server] [-f first_ttl] [-g gateway] [-i iface]\n" + "\t[-M first_ttl] [-m max_ttl] [-p port] [-P proto] [-q nqueries] [-s src_addr]\n" + "\t[-t tos] [-w waittime] [-z pausemsecs] host [packetlen]\n", prog); + exit(1); +} diff --git a/network_cmds/traceroute.tproj/traceroute.h b/network_cmds/traceroute.tproj/traceroute.h new file mode 100644 index 0000000..31154d8 --- /dev/null +++ b/network_cmds/traceroute.tproj/traceroute.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Id: traceroute.h,v 1.2 2004/08/08 00:27:54 lindak Exp $ (LBL) + */ + +extern char *prog; + +void setsin(struct sockaddr_in *, u_int32_t); diff --git a/network_cmds/traceroute.tproj/version.c b/network_cmds/traceroute.tproj/version.c new file mode 100644 index 0000000..680dcd4 --- /dev/null +++ b/network_cmds/traceroute.tproj/version.c @@ -0,0 +1 @@ +char version[] = "1.4a12+Darwin"; diff --git a/network_cmds/traceroute6.tproj/traceroute6.8 b/network_cmds/traceroute6.tproj/traceroute6.8 new file mode 100644 index 0000000..b5b220b --- /dev/null +++ b/network_cmds/traceroute6.tproj/traceroute6.8 @@ -0,0 +1,178 @@ +.\" $KAME: traceroute6.8,v 1.10 2004/06/06 12:35:15 suz Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.sbin/traceroute6/traceroute6.8,v 1.17 2008/02/10 21:06:38 dwmalone Exp $ +.\" +.Dd May 17, 1998 +.Dt TRACEROUTE6 8 +.Os +.\" +.Sh NAME +.Nm traceroute6 +.Nd "print the route IPv6 packets will take to a network node" +.\" +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl dIlnNrvU +.Ek +.Bk -words +.Op Fl f Ar firsthop +.Ek +.Bk -words +.Op Fl g Ar gateway +.Ek +.Bk -words +.Op Fl m Ar hoplimit +.Ek +.Bk -words +.Op Fl p Ar port +.Ek +.Bk -words +.Op Fl q Ar probes +.Ek +.Bk -words +.Op Fl s Ar src +.Ek +.Bk -words +.Op Fl w Ar waittime +.Ek +.Bk -words +.Ar target +.Op Ar datalen +.Ek +.\" +.Sh DESCRIPTION +The +.Nm +utility +uses the IPv6 protocol hop limit field to elicit an ICMPv6 TIME_EXCEEDED +response from each gateway along the path to some host. +.Pp +The only mandatory parameter is the destination host name or IPv6 address. +The default probe datagram carries 12 bytes of payload, +in addition to the IPv6 header. +The size of the payload can be specified by giving a length +(in bytes) +after the destination host name. +.Pp +Other options are: +.Bl -tag -width Ds +.It Fl d +Debug mode. +.It Fl f Ar firsthop +Specify how many hops to skip in trace. +.It Fl g Ar gateway +Specify intermediate gateway +.Nm ( +uses routing header). +.It Fl I +Use ICMP6 ECHO instead of UDP datagrams. +.It Fl l +Print both host hostnames and numeric addresses. +Normally +.Nm +prints only hostnames if +.Fl n +is not specified, and only numeric addresses if +.Fl n +is specified. +.It Fl m Ar hoplimit +Specify maximum hoplimit, up to 255. +The default is 30 hops. +.It Fl n +Do not resolve numeric address to hostname. +.It Fl N +Use a packet with no upper layer header for the probes, +instead of UDP datagrams. +.It Fl p Ar port +Set UDP port number to +.Ar port . +.It Fl q Ar probes +Set the number of probe per hop count to +.Ar probes . +.It Fl r +Bypass the normal routing tables and send directly to a host +on an attached network. +If the host is not on a directly-connected network, +an error is returned. +This option corresponds to the +.Dv SO_DONTROUTE +socket option; +it can be used to ping a local host through an interface +that has no route through it +(e.g., after the interface was dropped by a routing daemon). +.It Fl s Ar src +.Ar Src +specifies the source IPv6 address to be used. +.It Fl U +Use UDP datagrams for the probes. +This is the default. +.It Fl v +Be verbose. +.It Fl w Ar waittime +Specify the delay time between probes. +.El +.Pp +This program prints the route to the given destination and the round-trip +time to each gateway, in the same manner as traceroute. +.Pp +Here is a list of possible annotations after the round-trip time for each gateway: +.Bl -hang -offset indent +.It !N +Destination Unreachable - No Route to Host. +.It !P +Destination Unreachable - Administratively Prohibited. +.It !S +Destination Unreachable - Not a Neighbour. +.It !A +Destination Unreachable - Address Unreachable. +.It !\& +This is printed if the hop limit is <= 1 on a port unreachable message. +This means that the packet got to the destination, +but that the reply had a hop limit that was just large enough to +allow it to get back to the source of the traceroute6. +This was more interesting in the IPv4 case, +where some IP stack bugs could be identified by this behaviour. +.El +.\" +.Sh RETURN VALUES +The +.Nm +utility will exit with 0 on success, and non-zero on errors. +.\" +.Sh SEE ALSO +.Xr ping 8 , +.Xr ping6 8 , +.Xr traceroute 8 +.\" +.Sh HISTORY +The +.Nm +utility first appeared in WIDE hydrangea IPv6 protocol stack kit. diff --git a/network_cmds/traceroute6.tproj/traceroute6.c b/network_cmds/traceroute6.tproj/traceroute6.c new file mode 100644 index 0000000..1de7809 --- /dev/null +++ b/network_cmds/traceroute6.tproj/traceroute6.c @@ -0,0 +1,1423 @@ +/* $KAME: traceroute6.c,v 1.68 2004/01/25 11:16:12 suz Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef lint +__unused static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* + * traceroute host - trace the route ip packets follow going to "host". + * + * Attempt to trace the route an ip packet would follow to some + * internet host. We find out intermediate hops by launching probe + * packets with a small ttl (time to live) then listening for an + * icmp "time exceeded" reply from a gateway. We start our probes + * with a ttl of one and increase by one until we get an icmp "port + * unreachable" (which means we got to "host") or hit a max (which + * defaults to 30 hops & can be changed with the -m flag). Three + * probes (change with -q flag) are sent at each ttl setting and a + * line is printed showing the ttl, address of the gateway and + * round trip time of each probe. If the probe answers come from + * different gateways, the address of each responding system will + * be printed. If there is no response within a 5 sec. timeout + * interval (changed with the -w flag), a "*" is printed for that + * probe. + * + * Probe packets are UDP format. We don't want the destination + * host to process them so the destination port is set to an + * unlikely value (if some clod on the destination is using that + * value, it can be changed with the -p flag). + * + * A sample use might be: + * + * [yak 71]% traceroute nis.nsf.net. + * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet + * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms + * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms + * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms + * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + * + * Note that lines 2 & 3 are the same. This is due to a buggy + * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards + * packets with a zero ttl. + * + * A more interesting example is: + * + * [yak 72]% traceroute allspice.lcs.mit.edu. + * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms + * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms + * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms + * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms + * 12 * * * + * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms + * 14 * * * + * 15 * * * + * 16 * * * + * 17 * * * + * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + * + * (I start to see why I'm having so much trouble with mail to + * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away + * either don't send ICMP "time exceeded" messages or send them + * with a ttl too small to reach us. 14 - 17 are running the + * MIT C Gateway code that doesn't send "time exceeded"s. God + * only knows what's going on with 12. + * + * The silent gateway 12 in the above may be the result of a bug in + * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) + * sends an unreachable message using whatever ttl remains in the + * original datagram. Since, for gateways, the remaining ttl is + * zero, the icmp "time exceeded" is guaranteed to not make it back + * to us. The behavior of this bug is slightly more interesting + * when it appears on the destination system: + * + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms + * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms + * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms + * 7 * * * + * 8 * * * + * 9 * * * + * 10 * * * + * 11 * * * + * 12 * * * + * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + * + * Notice that there are 12 "gateways" (13 is the final + * destination) and exactly the last half of them are "missing". + * What's really happening is that rip (a Sun-3 running Sun OS3.5) + * is using the ttl from our arriving datagram as the ttl in its + * icmp reply. So, the reply will time out on the return path + * (with no notice sent to anyone since icmp's aren't sent for + * icmp's) until we probe with a ttl that's at least twice the path + * length. I.e., rip is really only 7 hops away. A reply that + * returns with a ttl of 1 is a clue this problem exists. + * Traceroute prints a "!" after the time if the ttl is <= 1. + * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or + * non-standard (HPUX) software, expect to see this problem + * frequently and/or take care picking the target host of your + * probes. + * + * Other possible annotations after the time are !H, !N, !P (got a host, + * network or protocol unreachable, respectively), !S or !F (source + * route failed or fragmentation needed -- neither of these should + * ever occur and the associated gateway is busted if you see one). If + * almost all the probes result in some kind of unreachable, traceroute + * will give up and exit. + * + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * This program requires a kernel mod that does not appear in any + * system available from Berkeley: A raw ip socket using proto + * IPPROTO_RAW must interpret the data sent as an ip datagram (as + * opposed to data to be wrapped in an ip datagram). See the README + * file that came with the source to this program for a description + * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may + * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE + * MODIFIED TO RUN THIS PROGRAM. + * + * The udp port usage may appear bizarre (well, ok, it is bizarre). + * The problem is that an icmp message only contains 8 bytes of + * data from the original datagram. 8 bytes is the size of a udp + * header so, if we want to associate replies with the original + * datagram, the necessary information must be encoded into the + * udp header (the ip id could be used but there's no way to + * interlock with the kernel's assignment of ip id's and, anyway, + * it would have taken a lot more kernel hacking to allow this + * code to set the ip id). So, to allow two or more users to + * use traceroute simultaneously, we use this task's pid as the + * source port (the high bit is set to move the port number out + * of the "likely" range). To keep track of which probe is being + * replied to (so times and/or hop counts don't get confused by a + * reply that was delayed in transit), we increment the destination + * port number before each probe. + * + * Don't use this as a coding example. I was trying to find a + * routing problem and this code sort-of popped out after 48 hours + * without sleep. I was amazed it ever compiled, much less ran. + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@helios.ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <netdb.h> +#include <stdio.h> +#include <err.h> +#ifdef HAVE_POLL +#include <poll.h> +#endif +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/udp.h> + +#ifdef IPSEC +#include <net/route.h> +#include <netinet6/ipsec.h> +#endif + +#define DUMMY_PORT 10010 + +#define MAXPACKET 65535 /* max ip packet size */ + +#ifndef HAVE_GETIPNODEBYNAME +#define getipnodebyname(x, y, z, u) gethostbyname2((x), (y)) +#define freehostent(x) +#endif + +/* + * format of a (udp) probe packet. + */ +struct tv32 { + u_int32_t tv32_sec; + u_int32_t tv32_usec; +}; + +struct opacket { + u_char seq; /* sequence number of this packet */ + u_char hops; /* hop limit of the packet */ + u_char pad[2]; + struct tv32 tv; /* time packet left */ +} __attribute__((__packed__)); + +u_char packet[512]; /* last inbound (icmp) packet */ +struct opacket *outpacket; /* last output (udp) packet */ + +int main(int, char *[]); +int wait_for_reply(int, struct msghdr *); +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +int setpolicy(int so, char *policy); +#endif +#endif +void send_probe(int, u_long); +void *get_uphdr(struct ip6_hdr *, u_char *); +int get_hoplim(struct msghdr *); +double deltaT(struct timeval *, struct timeval *); +char *pr_type(int); +int packet_ok(struct msghdr *, int, int); +void print(struct msghdr *, int); +const char *inetname(struct sockaddr *); +void usage(void); + +int rcvsock; /* receive (icmp) socket file descriptor */ +int sndsock; /* send (udp) socket file descriptor */ + +struct msghdr rcvmhdr; +struct iovec rcviov[2]; +int rcvhlim; +struct in6_pktinfo *rcvpktinfo; + +struct sockaddr_in6 Src, Dst, Rcv; +u_long datalen; /* How much data */ +#define ICMP6ECHOLEN 8 +/* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */ +char rtbuf[2064]; +#ifdef USE_RFC2292BIS +struct ip6_rthdr *rth; +#endif +struct cmsghdr *cmsg; + +char *source = 0; +char *hostname; + +u_long nprobes = 3; +u_long first_hop = 1; +u_long max_hops = 30; +u_int16_t srcport; +u_int16_t port = 32768+666; /* start udp dest port # for probe packets */ +u_int16_t ident; +int options; /* socket options */ +int verbose; +int waittime = 5; /* time to wait for response (in seconds) */ +int nflag; /* print addresses numerically */ +int useproto = IPPROTO_UDP; /* protocol to use to send packet */ +int lflag; /* print both numerical address & hostname */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int mib[4] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_DEFHLIM }; + char hbuf[NI_MAXHOST], src0[NI_MAXHOST], *ep; + int ch, i, on = 1, seq, rcvcmsglen, error, minlen; + struct addrinfo hints, *res; + static u_char *rcvcmsgbuf; + u_long probe, hops, lport; + struct hostent *hp; + size_t size; + + /* + * Receive ICMP + */ + if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + perror("socket(ICMPv6)"); + exit(5); + } + + size = sizeof(i); + (void) sysctl(mib, sizeof(mib)/sizeof(mib[0]), &i, &size, NULL, 0); + max_hops = i; + + /* specify to tell receiving interface */ +#ifdef IPV6_RECVPKTINFO + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_RECVPKTINFO)"); +#else /* old adv. API */ + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_PKTINFO)"); +#endif + + /* specify to tell value of hoplimit field of received IP6 hdr */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_RECVHOPLIMIT)"); +#else /* old adv. API */ + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_HOPLIMIT)"); +#endif + + seq = 0; + + while ((ch = getopt(argc, argv, "df:g:Ilm:nNp:q:rs:Uvw:")) != -1) + switch (ch) { + case 'd': + options |= SO_DEBUG; + break; + case 'f': + ep = NULL; + errno = 0; + first_hop = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep || first_hop > 255) { + fprintf(stderr, + "traceroute6: invalid min hoplimit.\n"); + exit(1); + } + break; + case 'g': + hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno); + if (hp == NULL) { + fprintf(stderr, + "traceroute6: unknown host %s\n", optarg); + exit(1); + } +#ifdef USE_RFC2292BIS + if (rth == NULL) { + /* + * XXX: We can't detect the number of + * intermediate nodes yet. + */ + if ((rth = inet6_rth_init((void *)rtbuf, + sizeof(rtbuf), IPV6_RTHDR_TYPE_0, + 0)) == NULL) { + fprintf(stderr, + "inet6_rth_init failed.\n"); + exit(1); + } + } + if (inet6_rth_add((void *)rth, + (struct in6_addr *)hp->h_addr)) { + fprintf(stderr, + "inet6_rth_add failed for %s\n", + optarg); + exit(1); + } +#else /* old advanced API */ + if (cmsg == NULL) + cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0); + inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr, + IPV6_RTHDR_LOOSE); +#endif + freehostent(hp); + break; + case 'I': + useproto = IPPROTO_ICMPV6; + ident = htons(getpid() & 0xffff); /* same as ping6 */ + break; + case 'l': + lflag++; + break; + case 'm': + ep = NULL; + errno = 0; + max_hops = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep || max_hops > 255) { + fprintf(stderr, + "traceroute6: invalid max hoplimit.\n"); + exit(1); + } + break; + case 'n': + nflag++; + break; + case 'N': + useproto = IPPROTO_NONE; + break; + case 'p': + ep = NULL; + errno = 0; + lport = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep) { + fprintf(stderr, "traceroute6: invalid port.\n"); + exit(1); + } + if (lport == 0 || lport != (lport & 0xffff)) { + fprintf(stderr, + "traceroute6: port out of range.\n"); + exit(1); + } + port = lport & 0xffff; + break; + case 'q': + ep = NULL; + errno = 0; + nprobes = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep) { + fprintf(stderr, + "traceroute6: invalid nprobes.\n"); + exit(1); + } + if (nprobes < 1) { + fprintf(stderr, + "traceroute6: nprobes must be >0.\n"); + exit(1); + } + break; + case 'r': + options |= SO_DONTROUTE; + break; + case 's': + /* + * set the ip source address of the outbound + * probe (e.g., on a multi-homed host). + */ + source = optarg; + break; + case 'v': + verbose++; + break; + case 'U': + useproto = IPPROTO_UDP; + break; + case 'w': + ep = NULL; + errno = 0; + waittime = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep) { + fprintf(stderr, + "traceroute6: invalid wait time.\n"); + exit(1); + } + if (waittime < 1) { + fprintf(stderr, + "traceroute6: wait must be >= 1 sec.\n"); + exit(1); + } + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * Open socket to send probe packets. + */ + switch (useproto) { + case IPPROTO_ICMPV6: + sndsock = rcvsock; + break; + case IPPROTO_UDP: + if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("socket(SOCK_DGRAM)"); + exit(5); + } + break; + case IPPROTO_NONE: + if ((sndsock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) { + perror("socket(SOCK_RAW)"); + exit(5); + } + break; + default: + fprintf(stderr, "traceroute6: unknown probe protocol %d", + useproto); + exit(5); + } + if (max_hops < first_hop) { + fprintf(stderr, + "traceroute6: max hoplimit must be larger than first hoplimit.\n"); + exit(1); + } + + /* revoke privs */ + seteuid(getuid()); + setuid(getuid()); + + if (argc < 1 || argc > 2) + usage(); + +#if 1 + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); +#else + setlinebuf(stdout); +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_RAW; + hints.ai_protocol = IPPROTO_ICMPV6; + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(*argv, NULL, &hints, &res); + if (error) { + fprintf(stderr, + "traceroute6: %s\n", gai_strerror(error)); + exit(1); + } + if (res->ai_addrlen != sizeof(Dst)) { + fprintf(stderr, + "traceroute6: size of sockaddr mismatch\n"); + exit(1); + } + memcpy(&Dst, res->ai_addr, res->ai_addrlen); + hostname = res->ai_canonname ? strdup(res->ai_canonname) : *argv; + if (!hostname) { + fprintf(stderr, "traceroute6: not enough core\n"); + exit(1); + } + if (res->ai_next) { + if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, + sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "?", sizeof(hbuf)); + fprintf(stderr, "traceroute6: Warning: %s has multiple " + "addresses; using %s\n", hostname, hbuf); + } + + if (*++argv) { + ep = NULL; + errno = 0; + datalen = strtoul(*argv, &ep, 0); + if (errno || !*argv || *ep) { + fprintf(stderr, + "traceroute6: invalid packet length.\n"); + exit(1); + } + } + switch (useproto) { + case IPPROTO_ICMPV6: + minlen = ICMP6ECHOLEN + sizeof(struct tv32); + break; + case IPPROTO_UDP: + minlen = sizeof(struct opacket); + break; + case IPPROTO_NONE: + minlen = 0; + datalen = 0; + break; + default: + fprintf(stderr, "traceroute6: unknown probe protocol %d.\n", + useproto); + exit(1); + } + if (datalen < minlen) + datalen = minlen; + else if (datalen >= MAXPACKET) { + fprintf(stderr, + "traceroute6: packet size must be %d <= s < %ld.\n", + minlen, (long)MAXPACKET); + exit(1); + } + outpacket = (struct opacket *)malloc((unsigned)datalen); + if (!outpacket) { + perror("malloc"); + exit(1); + } + (void) bzero((char *)outpacket, datalen); + + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = (caddr_t)packet; + rcviov[0].iov_len = sizeof(packet); + rcvmhdr.msg_name = (caddr_t)&Rcv; + rcvmhdr.msg_namelen = sizeof(Rcv); + rcvmhdr.msg_iov = rcviov; + rcvmhdr.msg_iovlen = 1; + rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + if ((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { + fprintf(stderr, "traceroute6: malloc failed\n"); + exit(1); + } + rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; + rcvmhdr.msg_controllen = rcvcmsglen; + + (void) setsockopt(rcvsock, SOL_SOCKET, SO_RECV_ANYIF, (char *)&on, + sizeof(on)); + if (options & SO_DEBUG) + (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + /* + * do not raise error even if setsockopt fails, kernel may have ipsec + * turned off. + */ + if (setpolicy(rcvsock, "in bypass") < 0) + errx(1, "%s", ipsec_strerror()); + if (setpolicy(rcvsock, "out bypass") < 0) + errx(1, "%s", ipsec_strerror()); +#else + { + int level = IPSEC_LEVEL_NONE; + + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, + sizeof(level)); + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, + sizeof(level)); +#ifdef IP_AUTH_TRANS_LEVEL + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, + sizeof(level)); +#else + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, + sizeof(level)); +#endif +#ifdef IP_AUTH_NETWORK_LEVEL + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, + sizeof(level)); +#endif + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + +#ifdef SO_SNDBUF + i = datalen; + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&i, + sizeof(i)) < 0 && useproto != IPPROTO_NONE) { + perror("setsockopt(SO_SNDBUF)"); + exit(6); + } +#endif /* SO_SNDBUF */ + if (options & SO_DEBUG) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); +#ifdef USE_RFC2292BIS + if (rth) {/* XXX: there is no library to finalize the header... */ + rth->ip6r_len = rth->ip6r_segleft * 2; + if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR, + (void *)rth, (rth->ip6r_len + 1) << 3)) { + fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n", + strerror(errno)); + exit(1); + } + } +#else /* old advanced API */ + if (cmsg != NULL) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS, + rtbuf, cmsg->cmsg_len) < 0) { + fprintf(stderr, "setsockopt(IPV6_PKTOPTIONS): %s\n", + strerror(errno)); + exit(1); + } + } +#endif /* USE_RFC2292BIS */ +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + /* + * do not raise error even if setsockopt fails, kernel may have ipsec + * turned off. + */ + if (setpolicy(sndsock, "in bypass") < 0) + errx(1, "%s", ipsec_strerror()); + if (setpolicy(sndsock, "out bypass") < 0) + errx(1, "%s", ipsec_strerror()); +#else + { + int level = IPSEC_LEVEL_BYPASS; + + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, + sizeof(level)); + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, + sizeof(level)); +#ifdef IP_AUTH_TRANS_LEVEL + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, + sizeof(level)); +#else + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, + sizeof(level)); +#endif +#ifdef IP_AUTH_NETWORK_LEVEL + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, + sizeof(level)); +#endif + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + + /* + * Source selection + */ + bzero(&Src, sizeof(Src)); + if (source) { + struct addrinfo hints, *res; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(source, "0", &hints, &res); + if (error) { + printf("traceroute6: %s: %s\n", source, + gai_strerror(error)); + exit(1); + } + if (res->ai_addrlen > sizeof(Src)) { + printf("traceroute6: %s: %s\n", source, + gai_strerror(error)); + exit(1); + } + memcpy(&Src, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } else { + struct sockaddr_in6 Nxt; + int dummy; + socklen_t len; + + Nxt = Dst; + Nxt.sin6_port = htons(DUMMY_PORT); + if (cmsg != NULL) + bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr, + sizeof(Nxt.sin6_addr)); + if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + if (connect(dummy, (struct sockaddr *)&Nxt, Nxt.sin6_len) < 0) { + perror("connect"); + exit(1); + } + len = sizeof(Src); + if (getsockname(dummy, (struct sockaddr *)&Src, &len) < 0) { + perror("getsockname"); + exit(1); + } + if (getnameinfo((struct sockaddr *)&Src, Src.sin6_len, + src0, sizeof(src0), NULL, 0, NI_NUMERICHOST)) { + fprintf(stderr, "getnameinfo failed for source\n"); + exit(1); + } + source = src0; + close(dummy); + } + + Src.sin6_port = htons(0); + if (bind(sndsock, (struct sockaddr *)&Src, Src.sin6_len) < 0) { + perror("bind"); + exit(1); + } + + { + socklen_t len; + + len = sizeof(Src); + if (getsockname(sndsock, (struct sockaddr *)&Src, &len) < 0) { + perror("getsockname"); + exit(1); + } + srcport = ntohs(Src.sin6_port); + } + + /* + * Message to users + */ + if (getnameinfo((struct sockaddr *)&Dst, Dst.sin6_len, hbuf, + sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) + strlcpy(hbuf, "(invalid)", sizeof(hbuf)); + fprintf(stderr, "traceroute6"); + fprintf(stderr, " to %s (%s)", hostname, hbuf); + if (source) + fprintf(stderr, " from %s", source); + fprintf(stderr, ", %lu hops max, %lu byte packets\n", + max_hops, datalen); + (void) fflush(stderr); + + if (first_hop > 1) + printf("Skipping %lu intermediate hops\n", first_hop - 1); + + /* + * Main loop + */ + for (hops = first_hop; hops <= max_hops; ++hops) { + struct in6_addr lastaddr; + int got_there = 0; + int unreachable = 0; + + printf("%2lu ", hops); + bzero(&lastaddr, sizeof(lastaddr)); + for (probe = 0; probe < nprobes; ++probe) { + int cc; + struct timeval t1, t2; + + (void) gettimeofday(&t1, NULL); + send_probe(++seq, hops); + while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) { + (void) gettimeofday(&t2, NULL); + if ((i = packet_ok(&rcvmhdr, cc, seq))) { + if (!IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr, + &lastaddr)) { + if (probe > 0) + fputs("\n ", stdout); + print(&rcvmhdr, cc); + lastaddr = Rcv.sin6_addr; + } + printf(" %.3f ms", deltaT(&t1, &t2)); + switch (i - 1) { + case ICMP6_DST_UNREACH_NOROUTE: + ++unreachable; + printf(" !N"); + break; + case ICMP6_DST_UNREACH_ADMIN: + ++unreachable; + printf(" !P"); + break; + case ICMP6_DST_UNREACH_NOTNEIGHBOR: + ++unreachable; + printf(" !S"); + break; + case ICMP6_DST_UNREACH_ADDR: + ++unreachable; + printf(" !A"); + break; + case ICMP6_DST_UNREACH_NOPORT: + if (rcvhlim >= 0 && + rcvhlim <= 1) + printf(" !"); + ++got_there; + break; + } + break; + } + } + if (cc == 0) + printf(" *"); + (void) fflush(stdout); + } + putchar('\n'); + if (got_there || + (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) { + exit(0); + } + } + + exit(0); +} + +int +wait_for_reply(sock, mhdr) + int sock; + struct msghdr *mhdr; +{ +#ifdef HAVE_POLL + struct pollfd pfd[1]; + int cc = 0; + + pfd[0].fd = sock; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + if (poll(pfd, 1, waittime * 1000) > 0) + cc = recvmsg(rcvsock, mhdr, 0); + + return(cc); +#else + fd_set *fdsp; + struct timeval wait; + int cc = 0, fdsn; + + fdsn = howmany(sock + 1, NFDBITS) * sizeof(fd_mask); + if ((fdsp = (fd_set *)malloc(fdsn)) == NULL) + err(1, "malloc"); + memset(fdsp, 0, fdsn); + FD_SET(sock, fdsp); + wait.tv_sec = waittime; wait.tv_usec = 0; + + if (select(sock+1, fdsp, (fd_set *)0, (fd_set *)0, &wait) > 0) + cc = recvmsg(rcvsock, mhdr, 0); + + free(fdsp); + return(cc); +#endif +} + +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +int +setpolicy(so, policy) + int so; + char *policy; +{ + char *buf; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + warnx("%s", ipsec_strerror()); + return -1; + } + (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)); + + free(buf); + + return 0; +} +#endif +#endif + +void +send_probe(seq, hops) + int seq; + u_long hops; +{ + struct icmp6_hdr *icp; + struct opacket *op; + struct timeval tv; + struct tv32 tv32; + int i; + + i = hops; + if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (char *)&i, sizeof(i)) < 0) { + perror("setsockopt IPV6_UNICAST_HOPS"); + } + + Dst.sin6_port = htons(port + seq); + (void) gettimeofday(&tv, NULL); + tv32.tv32_sec = htonl(tv.tv_sec); + tv32.tv32_usec = htonl(tv.tv_usec); + + switch (useproto) { + case IPPROTO_ICMPV6: + icp = (struct icmp6_hdr *)outpacket; + + icp->icmp6_type = ICMP6_ECHO_REQUEST; + icp->icmp6_code = 0; + icp->icmp6_cksum = 0; + icp->icmp6_id = ident; + icp->icmp6_seq = htons(seq); + bcopy(&tv32, ((u_int8_t *)outpacket + ICMP6ECHOLEN), + sizeof(tv32)); + break; + case IPPROTO_UDP: + op = outpacket; + + op->seq = seq; + op->hops = hops; + bcopy(&tv32, &op->tv, sizeof tv32); + break; + case IPPROTO_NONE: + /* No space for anything. No harm as seq/tv32 are decorative. */ + break; + default: + fprintf(stderr, "Unknown probe protocol %d.\n", useproto); + exit(1); + } + + i = sendto(sndsock, (char *)outpacket, datalen, 0, + (struct sockaddr *)&Dst, Dst.sin6_len); + if (i < 0 || i != datalen) { + if (i < 0) + perror("sendto"); + printf("traceroute6: wrote %s %lu chars, ret=%d\n", + hostname, datalen, i); + (void) fflush(stdout); + } +} + +int +get_hoplim(mhdr) + struct msghdr *mhdr; +{ + struct cmsghdr *cm; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + return(*(int *)CMSG_DATA(cm)); + } + + return(-1); +} + +double +deltaT(t1p, t2p) + struct timeval *t1p, *t2p; +{ + double dt; + + dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + + (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; + return (dt); +} + +/* + * Convert an ICMP "type" field to a printable string. + */ +char * +pr_type(t0) + int t0; +{ + u_char t = t0 & 0xff; + char *cp; + + switch (t) { + case ICMP6_DST_UNREACH: + cp = "Destination Unreachable"; + break; + case ICMP6_PACKET_TOO_BIG: + cp = "Packet Too Big"; + break; + case ICMP6_TIME_EXCEEDED: + cp = "Time Exceeded"; + break; + case ICMP6_PARAM_PROB: + cp = "Parameter Problem"; + break; + case ICMP6_ECHO_REQUEST: + cp = "Echo Request"; + break; + case ICMP6_ECHO_REPLY: + cp = "Echo Reply"; + break; + case ICMP6_MEMBERSHIP_QUERY: + cp = "Group Membership Query"; + break; + case ICMP6_MEMBERSHIP_REPORT: + cp = "Group Membership Report"; + break; + case ICMP6_MEMBERSHIP_REDUCTION: + cp = "Group Membership Reduction"; + break; + case ND_ROUTER_SOLICIT: + cp = "Router Solicitation"; + break; + case ND_ROUTER_ADVERT: + cp = "Router Advertisement"; + break; + case ND_NEIGHBOR_SOLICIT: + cp = "Neighbor Solicitation"; + break; + case ND_NEIGHBOR_ADVERT: + cp = "Neighbor Advertisement"; + break; + case ND_REDIRECT: + cp = "Redirect"; + break; + default: + cp = "Unknown"; + break; + } + return cp; +} + +int +packet_ok(mhdr, cc, seq) + struct msghdr *mhdr; + int cc; + int seq; +{ + struct icmp6_hdr *icp; + struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; + u_char type, code; + char *buf = (char *)mhdr->msg_iov[0].iov_base; + struct cmsghdr *cm; + int *hlimp; + char hbuf[NI_MAXHOST]; + +#ifdef OLDRAWSOCKET + int hlen; + struct ip6_hdr *ip; +#endif + +#ifdef OLDRAWSOCKET + ip = (struct ip6_hdr *) buf; + hlen = sizeof(struct ip6_hdr); + if (cc < hlen + sizeof(struct icmp6_hdr)) { + if (verbose) { + if (getnameinfo((struct sockaddr *)from, from->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + printf("packet too short (%d bytes) from %s\n", cc, + hbuf); + } + return (0); + } + cc -= hlen; + icp = (struct icmp6_hdr *)(buf + hlen); +#else + if (cc < sizeof(struct icmp6_hdr)) { + if (verbose) { + if (getnameinfo((struct sockaddr *)from, from->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + printf("data too short (%d bytes) from %s\n", cc, hbuf); + } + return(0); + } + icp = (struct icmp6_hdr *)buf; +#endif + /* get optional information via advanced API */ + rcvpktinfo = NULL; + hlimp = NULL; + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == + CMSG_LEN(sizeof(struct in6_pktinfo))) + rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm)); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *)CMSG_DATA(cm); + } + if (rcvpktinfo == NULL || hlimp == NULL) { + warnx("failed to get received hop limit or packet info"); +#if 0 + return(0); +#else + rcvhlim = 0; /*XXX*/ +#endif + } + else + rcvhlim = *hlimp; + + type = icp->icmp6_type; + code = icp->icmp6_code; + if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) + || type == ICMP6_DST_UNREACH) { + struct ip6_hdr *hip; + void *up; + + hip = (struct ip6_hdr *)(icp + 1); + if ((up = get_uphdr(hip, (u_char *)(buf + cc))) == NULL) { + if (verbose) + warnx("failed to get upper layer header"); + return(0); + } + switch (useproto) { + case IPPROTO_ICMPV6: + if (((struct icmp6_hdr *)up)->icmp6_id == ident && + ((struct icmp6_hdr *)up)->icmp6_seq == htons(seq)) + return (type == ICMP6_TIME_EXCEEDED ? + -1 : code + 1); + break; + case IPPROTO_UDP: + if (((struct udphdr *)up)->uh_sport == htons(srcport) && + ((struct udphdr *)up)->uh_dport == htons(port + seq)) + return (type == ICMP6_TIME_EXCEEDED ? + -1 : code + 1); + break; + case IPPROTO_NONE: + return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1); + default: + fprintf(stderr, "Unknown probe proto %d.\n", useproto); + break; + } + } else if (useproto == IPPROTO_ICMPV6 && type == ICMP6_ECHO_REPLY) { + if (icp->icmp6_id == ident && + icp->icmp6_seq == htons(seq)) + return (ICMP6_DST_UNREACH_NOPORT + 1); + } + if (verbose) { + char sbuf[NI_MAXHOST+1], dbuf[INET6_ADDRSTRLEN]; + u_int8_t *p; + int i; + + if (getnameinfo((struct sockaddr *)from, from->sin6_len, + sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(sbuf, "invalid", sizeof(sbuf)); + printf("\n%d bytes from %s to %s", cc, sbuf, + rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, + dbuf, sizeof(dbuf)) : "?"); + printf(": icmp type %d (%s) code %d\n", type, pr_type(type), + icp->icmp6_code); + p = (u_int8_t *)(icp + 1); +#define WIDTH 16 + for (i = 0; i < cc; i++) { + if (i % WIDTH == 0) + printf("%04x:", i); + if (i % 4 == 0) + printf(" "); + printf("%02x", p[i]); + if (i % WIDTH == WIDTH - 1) + printf("\n"); + } + if (cc % WIDTH != 0) + printf("\n"); + } + return(0); +} + +/* + * Increment pointer until find the UDP or ICMP header. + */ +void * +get_uphdr(ip6, lim) + struct ip6_hdr *ip6; + u_char *lim; +{ + u_char *cp = (u_char *)ip6, nh; + int hlen; + static u_char none_hdr[1]; /* Fake pointer for IPPROTO_NONE. */ + + if (cp + sizeof(*ip6) > lim) + return(NULL); + + nh = ip6->ip6_nxt; + cp += sizeof(struct ip6_hdr); + + while (lim - cp >= (nh == IPPROTO_NONE ? 0 : 8)) { + switch (nh) { + case IPPROTO_ESP: + case IPPROTO_TCP: + return(NULL); + case IPPROTO_ICMPV6: + return(useproto == nh ? cp : NULL); + case IPPROTO_UDP: + return(useproto == nh ? cp : NULL); + case IPPROTO_NONE: + return(useproto == nh ? none_hdr : NULL); + case IPPROTO_FRAGMENT: + hlen = sizeof(struct ip6_frag); + nh = ((struct ip6_frag *)cp)->ip6f_nxt; + break; + case IPPROTO_AH: + hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2; + nh = ((struct ip6_ext *)cp)->ip6e_nxt; + break; + default: + hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3; + nh = ((struct ip6_ext *)cp)->ip6e_nxt; + break; + } + + cp += hlen; + } + + return(NULL); +} + +void +print(mhdr, cc) + struct msghdr *mhdr; + int cc; +{ + struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; + char hbuf[NI_MAXHOST]; + + if (getnameinfo((struct sockaddr *)from, from->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + if (nflag) + printf(" %s", hbuf); + else if (lflag) + printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf); + else + printf(" %s", inetname((struct sockaddr *)from)); + + if (verbose) { +#ifdef OLDRAWSOCKET + printf(" %d bytes to %s", cc, + rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, + hbuf, sizeof(hbuf)) : "?"); +#else + printf(" %d bytes of data to %s", cc, + rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, + hbuf, sizeof(hbuf)) : "?"); +#endif + } +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +const char * +inetname(sa) + struct sockaddr *sa; +{ + static char line[NI_MAXHOST], domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + char *cp; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, sizeof(domain)) == 0 && + (cp = strchr(domain, '.'))) + (void) memmove(domain, cp + 1, strlen(cp + 1) + 1); + else + domain[0] = 0; + } + cp = NULL; + if (!nflag) { + if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, + NI_NAMEREQD) == 0) { + if ((cp = strchr(line, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = line; + } + } + if (cp) + return cp; + + if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, + NI_NUMERICHOST) != 0) + strlcpy(line, "invalid", sizeof(line)); + return line; +} + +void +usage() +{ + + fprintf(stderr, +"usage: traceroute6 [-dIlnNrUv] [-f firsthop] [-g gateway] [-m hoplimit]\n" +" [-p port] [-q probes] [-s src] [-w waittime] target [datalen]\n"); + exit(1); +} |