├── .gitignore ├── COPYING.rtf ├── HoRNDIS-Info.plist ├── HoRNDIS-Prefix.pch ├── HoRNDIS.cpp ├── HoRNDIS.h ├── HoRNDIS.xcodeproj └── project.pbxproj ├── KNOWN_BUGS ├── Makefile ├── README.md ├── en.lproj └── InfoPlist.strings ├── package ├── Distribution.xml ├── intro-text.rtf ├── post-readme.rtf └── scripts │ └── postinstall └── test_kext.command /.gitignore: -------------------------------------------------------------------------------- 1 | InfoPlist.h 2 | 3 | # Created by https://www.gitignore.io 4 | 5 | ### Xcode ### 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.xcuserstate 20 | 21 | 22 | ### OSX ### 23 | .DS_Store 24 | .AppleDouble 25 | .LSOverride 26 | 27 | # Icon must end with two \r 28 | Icon 29 | 30 | 31 | # Resource Forks 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | 46 | ### C++ ### 47 | # Compiled Object files 48 | *.slo 49 | *.lo 50 | *.o 51 | *.obj 52 | 53 | # Precompiled Headers 54 | *.gch 55 | 56 | # Compiled Dynamic libraries 57 | *.so 58 | *.dylib 59 | *.dll 60 | 61 | # Fortran module files 62 | *.mod 63 | 64 | # Compiled Static libraries 65 | *.lai 66 | *.la 67 | *.a 68 | *.lib 69 | -------------------------------------------------------------------------------- /COPYING.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 2 | {\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\fnil\fcharset0 Menlo-Regular;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{lower-alpha\})}{\leveltext\leveltemplateid1\'02\'00);}{\levelnumbers\'01;}\fi-360\li720\lin720 }{\listname ;}\listid1} 5 | {\list\listtemplateid2\listhybrid{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{lower-alpha\})}{\leveltext\leveltemplateid101\'02\'00);}{\levelnumbers\'01;}\fi-360\li720\lin720 }{\listname ;}\listid2} 6 | {\list\listtemplateid3\listhybrid{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{lower-alpha\})}{\leveltext\leveltemplateid201\'02\'00);}{\levelnumbers\'01;}\fi-360\li720\lin720 }{\listname ;}\listid3}} 7 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}} 8 | \margl1440\margr1440\vieww10800\viewh8400\viewkind0 9 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 10 | 11 | \f0\fs24 \cf0 \ 12 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc 13 | 14 | \b\fs36 \cf0 GNU GENERAL PUBLIC LICENSE\ 15 | 16 | \b0\fs24 \ 17 | Version 3, 29 June 2007\ 18 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 19 | \cf0 \ 20 | Copyright \'a9 2007 Free Software Foundation, Inc.\ 21 | <{\field{\*\fldinst{HYPERLINK "http://fsf.org/"}}{\fldrslt http://fsf.org/}}>\ 22 | \ 23 | Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\ 24 | \ 25 | 26 | \b\fs36 Preamble\ 27 | 28 | \b0\fs24 \ 29 | The GNU General Public License is a free, copyleft license for software and other kinds of works.\ 30 | \ 31 | The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.\ 32 | \ 33 | When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.\ 34 | \ 35 | To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.\ 36 | \ 37 | For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\ 38 | \ 39 | Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.\ 40 | \ 41 | For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.\ 42 | \ 43 | Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.\ 44 | \ 45 | Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.\ 46 | \ 47 | The precise terms and conditions for copying, distribution and modification follow.\ 48 | \ 49 | 50 | \b\fs36 TERMS AND CONDITIONS\ 51 | \ 52 | 53 | \fs28 0. Definitions.\ 54 | 55 | \b0\fs24 \ 56 | \'93This License\'94 refers to version 3 of the GNU General Public License.\ 57 | \ 58 | \'93Copyright\'94 also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.\ 59 | \ 60 | \'93The Program\'94 refers to any copyrightable work licensed under this License. Each licensee is addressed as \'93you\'94. \'93Licensees\'94 and \'93recipients\'94 may be individuals or organizations.\ 61 | \ 62 | To \'93modify\'94 a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a \'93modified version\'94 of the earlier work or a work \'93based on\'94 the earlier work.\ 63 | \ 64 | A \'93covered work\'94 means either the unmodified Program or a work based on the Program.\ 65 | \ 66 | To \'93propagate\'94 a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.\ 67 | \ 68 | To \'93convey\'94 a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.\ 69 | \ 70 | An interactive user interface displays \'93Appropriate Legal Notices\'94 to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.\ 71 | \ 72 | 73 | \b\fs28 1. Source Code.\ 74 | 75 | \b0\fs24 \ 76 | The \'93source code\'94 for a work means the preferred form of the work for making modifications to it. \'93Object code\'94 means any non-source form of a work.\ 77 | \ 78 | A \'93Standard Interface\'94 means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.\ 79 | \ 80 | The \'93System Libraries\'94 of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A \'93Major Component\'94, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.\ 81 | \ 82 | The \'93Corresponding Source\'94 for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.\ 83 | \ 84 | The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.\ 85 | \ 86 | The Corresponding Source for a work in source code form is that same work.\ 87 | \ 88 | 89 | \b\fs28 2. Basic Permissions.\ 90 | 91 | \b0\fs24 \ 92 | All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.\ 93 | \ 94 | You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.\ 95 | \ 96 | Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.\ 97 | \ 98 | 99 | \b\fs28 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\ 100 | 101 | \b0\fs24 \ 102 | No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.\ 103 | \ 104 | When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.\ 105 | \ 106 | 107 | \b\fs28 4. Conveying Verbatim Copies.\ 108 | 109 | \b0\fs24 \ 110 | You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.\ 111 | \ 112 | You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.\ 113 | \ 114 | 115 | \b\fs28 5. Conveying Modified Source Versions.\ 116 | 117 | \b0\fs24 \ 118 | You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:\ 119 | \ 120 | \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural 121 | \ls1\ilvl0\cf0 {\listtext a) }The work must carry prominent notices stating that you modified it, and giving a relevant date.\ 122 | {\listtext b) }The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to \'93keep intact all notices\'94.\ 123 | {\listtext c) }You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.\ 124 | {\listtext d) }If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.\ 125 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 126 | \cf0 \ 127 | A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an \'93aggregate\'94 if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.\ 128 | \ 129 | 130 | \b\fs28 6. Conveying Non-Source Forms.\ 131 | 132 | \b0\fs24 \ 133 | You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:\ 134 | \ 135 | \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural 136 | \ls2\ilvl0\cf0 {\listtext a) }Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.\ 137 | {\listtext b) }Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.\ 138 | {\listtext c) }Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.\ 139 | {\listtext d) }Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.\ 140 | {\listtext e) }Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.\ 141 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 142 | \cf0 \ 143 | A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.\ 144 | \ 145 | A \'93User Product\'94 is either (1) a \'93consumer product\'94, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, \'93normally used\'94 refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.\ 146 | \ 147 | \'93Installation Information\'94 for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.\ 148 | \ 149 | If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).\ 150 | \ 151 | The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.\ 152 | \ 153 | Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.\ 154 | \ 155 | 156 | \b\fs28 7. Additional Terms.\ 157 | 158 | \b0\fs24 \ 159 | \'93Additional permissions\'94 are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.\ 160 | \ 161 | When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.\ 162 | \ 163 | Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:\ 164 | \ 165 | \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural 166 | \ls3\ilvl0\cf0 {\listtext a) }Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or\ 167 | {\listtext b) }Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or\ 168 | {\listtext c) }Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or\ 169 | {\listtext d) }Limiting the use for publicity purposes of names of licensors or authors of the material; or\ 170 | {\listtext e) }Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or\ 171 | {\listtext f) }Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.\ 172 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 173 | \cf0 \ 174 | All other non-permissive additional terms are considered \'93further restrictions\'94 within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.\ 175 | \ 176 | If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.\ 177 | \ 178 | Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.\ 179 | \ 180 | 181 | \b\fs28 8. Termination.\ 182 | 183 | \b0\fs24 \ 184 | You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).\ 185 | \ 186 | However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.\ 187 | \ 188 | Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.\ 189 | \ 190 | Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.\ 191 | \ 192 | 193 | \b\fs28 9. Acceptance Not Required for Having Copies.\ 194 | 195 | \b0\fs24 \ 196 | You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.\ 197 | \ 198 | 199 | \b\fs28 10. Automatic Licensing of Downstream Recipients.\ 200 | 201 | \b0\fs24 \ 202 | Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.\ 203 | \ 204 | An \'93entity transaction\'94 is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.\ 205 | \ 206 | You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.\ 207 | \ 208 | 209 | \b\fs28 11. Patents.\ 210 | 211 | \b0\fs24 \ 212 | A \'93contributor\'94 is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's \'93contributor version\'94.\ 213 | \ 214 | A contributor's \'93essential patent claims\'94 are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, \'93control\'94 includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.\ 215 | \ 216 | Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.\ 217 | \ 218 | In the following three paragraphs, a \'93patent license\'94 is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To \'93grant\'94 such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.\ 219 | \ 220 | If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. \'93Knowingly relying\'94 means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.\ 221 | \ 222 | If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.\ 223 | \ 224 | A patent license is \'93discriminatory\'94 if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.\ 225 | \ 226 | Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.\ 227 | \ 228 | 229 | \b\fs28 12. No Surrender of Others' Freedom.\ 230 | 231 | \b0\fs24 \ 232 | If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.\ 233 | \ 234 | 235 | \b\fs28 13. Use with the GNU Affero General Public License.\ 236 | 237 | \b0\fs24 \ 238 | Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.\ 239 | \ 240 | 241 | \b\fs28 14. Revised Versions of this License.\ 242 | 243 | \b0\fs24 \ 244 | The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\ 245 | \ 246 | Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License \'93or any later version\'94 applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.\ 247 | \ 248 | If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.\ 249 | \ 250 | Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.\ 251 | \ 252 | 253 | \b\fs28 15. Disclaimer of Warranty.\ 254 | 255 | \b0\fs24 \ 256 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \'93AS IS\'94 WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\ 257 | \ 258 | 259 | \b\fs28 16. Limitation of Liability.\ 260 | 261 | \b0\fs24 \ 262 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\ 263 | \ 264 | 265 | \b\fs28 17. Interpretation of Sections 15 and 16.\ 266 | 267 | \b0\fs24 \ 268 | If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.\ 269 | \ 270 | END OF TERMS AND CONDITIONS\ 271 | \ 272 | 273 | \b\fs36 How to Apply These Terms to Your New Programs\ 274 | 275 | \b0\fs24 \ 276 | If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\ 277 | \ 278 | To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the \'93copyright\'94 line and a pointer to where the full notice is found.\ 279 | \ 280 | 281 | \f1 \ 282 | Copyright (C) \ 283 | \ 284 | This program is free software: you can redistribute it and/or modify\ 285 | it under the terms of the GNU General Public License as published by\ 286 | the Free Software Foundation, either version 3 of the License, or\ 287 | (at your option) any later version.\ 288 | \ 289 | This program is distributed in the hope that it will be useful,\ 290 | but WITHOUT ANY WARRANTY; without even the implied warranty of\ 291 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\ 292 | GNU General Public License for more details.\ 293 | \ 294 | You should have received a copy of the GNU General Public License\ 295 | along with this program. If not, see .\ 296 | 297 | \f0 \ 298 | Also add information on how to contact you by electronic and paper mail.\ 299 | \ 300 | If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:\ 301 | \ 302 | 303 | \f1 Copyright (C) \ 304 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\ 305 | This is free software, and you are welcome to redistribute it\ 306 | under certain conditions; type `show c' for details.\ 307 | 308 | \f0 \ 309 | The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an \'93about box\'94.\ 310 | \ 311 | You should also get your employer (if you work as a programmer) or school, if any, to sign a \'93copyright disclaimer\'94 for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <{\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}>.\ 312 | \ 313 | The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <{\field{\*\fldinst{HYPERLINK "http://www.gnu.org/philosophy/why-not-lgpl.html"}}{\fldrslt http://www.gnu.org/philosophy/why-not-lgpl.html}}>.\ 314 | } -------------------------------------------------------------------------------- /HoRNDIS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleGetInfoString 10 | Android/RNDIS tethering driver 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | KEXT 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(MODULE_VERSION) 25 | IOKitPersonalities 26 | 27 | RNDISControlStockAndroid 28 | 29 | CFBundleIdentifier 30 | com.joshuawise.kexts.HoRNDIS 31 | IOClass 32 | HoRNDIS 33 | IOProviderClass 34 | IOUSBHostInterface 35 | bInterfaceClass 36 | 224 37 | bInterfaceSubClass 38 | 1 39 | bInterfaceProtocol 40 | 3 41 | 42 | RNDISControlMiscDeviceRoE 43 | 44 | CFBundleIdentifier 45 | com.joshuawise.kexts.HoRNDIS 46 | IOClass 47 | HoRNDIS 48 | IOProviderClass 49 | IOUSBHostInterface 50 | bInterfaceClass 51 | 239 52 | bInterfaceSubClass 53 | 4 54 | bInterfaceProtocol 55 | 1 56 | 57 | RNDISControlLinuxGadget 58 | 59 | CFBundleIdentifier 60 | com.joshuawise.kexts.HoRNDIS 61 | IOClass 62 | HoRNDIS 63 | IOProviderClass 64 | IOUSBHostInterface 65 | bInterfaceClass 66 | 2 67 | bInterfaceSubClass 68 | 2 69 | bInterfaceProtocol 70 | 255 71 | 72 | WirelessControllerDevice 73 | 74 | CFBundleIdentifier 75 | com.joshuawise.kexts.HoRNDIS 76 | IOClass 77 | HoRNDIS 78 | IOProviderClass 79 | IOUSBHostDevice 80 | bDeviceClass 81 | 224 82 | bDeviceSubClass 83 | 0 84 | bDeviceProtocol 85 | 0 86 | 87 | 88 | OSBundleLibraries 89 | 90 | com.apple.iokit.IONetworkingFamily 91 | 3.2 92 | com.apple.iokit.IOUSBHostFamily 93 | 1.0.1 94 | com.apple.kpi.bsd 95 | 15.0 96 | com.apple.kpi.iokit 97 | 15.0 98 | com.apple.kpi.libkern 99 | 15.0 100 | com.apple.kpi.mach 101 | 15.0 102 | com.apple.kpi.unsupported 103 | 15.0 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /HoRNDIS-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'HoRNDIS' target in the 'HoRNDIS' project 3 | // 4 | 5 | -------------------------------------------------------------------------------- /HoRNDIS.cpp: -------------------------------------------------------------------------------- 1 | /* HoRNDIS.cpp 2 | * Implementation of IOKit-derived classes 3 | * HoRNDIS, a RNDIS driver for Mac OS X 4 | * 5 | * Copyright (c) 2012 Joshua Wise. 6 | * Copyright (c) 2018 Mikhail Iakhiaev 7 | * 8 | * IOKit examples from Apple's USBCDCEthernet.cpp; not much of that code remains. 9 | * 10 | * RNDIS logic is from linux/drivers/net/usb/rndis_host.c, which is: 11 | * 12 | * Copyright (c) 2005 David Brownell. 13 | * 14 | * 15 | * This program is free software; you can redistribute it and/or modify 16 | * it under the terms of the GNU General Public License as published by 17 | * the Free Software Foundation; either version 2 of the License, or 18 | * (at your option) any later version. 19 | * 20 | * This program is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * You should have received a copy of the GNU General Public License 26 | * along with this program; if not, write to the Free Software 27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 | */ 29 | 30 | #include "HoRNDIS.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | // May be useful for supporting suspend/resume: 40 | // #include 41 | 42 | 43 | #define V_PTR 0 44 | #define V_PACKET 1 45 | #define V_DEBUG 2 46 | #define V_NOTE 3 47 | #define V_ERROR 4 48 | 49 | // The XCode "Debug" build is now more verbose: 50 | #if DEBUG == 1 51 | #define DEBUGLEVEL V_DEBUG 52 | #else 53 | #define DEBUGLEVEL V_NOTE 54 | #endif 55 | #define LOG(verbosity, s, ...) do { if (verbosity >= DEBUGLEVEL) IOLog("HoRNDIS: %s: " s "\n", __func__, ##__VA_ARGS__); } while(0) 56 | 57 | #define super IOEthernetController 58 | 59 | OSDefineMetaClassAndStructors(HoRNDIS, IOEthernetController); 60 | OSDefineMetaClassAndStructors(HoRNDISInterface, IOEthernetInterface); 61 | 62 | /* 63 | ================================================================ 64 | DESCRIPTION OF DEVICE DRIVER MATCHING (+ Info.plist description) 65 | ================================================================ 66 | The HoRNDIS driver classes are only instantiated when the MacOS matches 67 | "IOKitPersonalities" dictionary entries to the existing devices. The matching 68 | can be done 2 based on two different provider classes: 69 | 70 | - IOUSBHostInterface - matches an interface under a USB device. Here, we match 71 | based on the interface class/subclass/protocol. In order for the matching to 72 | work, some other driver has to open the USB device and call 73 | "setConfiguration" method with matchInterfaces=true. 74 | The interface matching works out-of-the-box for interfaces under USB 75 | Composite Devices (class/subclass/protocol are 0/0/0), since there is an OS 76 | driver that opens such devices for matching. 77 | 78 | - IOUSBHostDevice - match is performed on the whole device. Here, we match 79 | based on class/subclass/protocol. The start method needs to call 80 | 'setConfiguration' in order for any IOUSBHostInterface instances under the 81 | device to become available. If it specifies 'matchInterfaces', matching is 82 | then performed on newly-created IOUSBHostInterfaces. 83 | 84 | OUR APPROACH: 85 | We'll match based on either device or interface, and let the probe and start 86 | methods handle the difference. Not calling setConfiguration with matchInterfaces 87 | for now: if it's not a USB composite device, consider that we own it. 88 | 89 | Subsequent logic: 90 | After MacOS finds a match based on Info.plist, it instantiates the driver class, 91 | and calls the 'probe' method that looks at descriptors and decides if this is 92 | really the device we care about (e.g. fine-grained filtering), and sets the 93 | "probeXxx" variables. Then, 'start' method calls 'openUSBInterfaces' that 94 | blindly follows the "probeXxx" variables to get the needed IOUSBHostInterface 95 | values from the opened device. 96 | 97 | ========================== 98 | || DEVICE VARIATIONS 99 | ========================== 100 | This section must document ALL different variations of the devices that we 101 | may be dealing with, so we have the whole picture when updating the "probe" 102 | code or "Info.plist". Notation: 103 | * Device: 224 / 0 / 0 104 | - bDeviceClass / bDeviceSubClass / bDeviceProtocol 105 | * Interface Associaton[2]: 224 / 1 / 3 106 | - [bInterfaceCount]: bFunctionClass / bFunctionSubClass / bFunctionProtocol 107 | * Interface: 224 / 3 / 1 108 | - bInterfaceClass / bInterfaceSubClass / bInterfaceProtocol 109 | 110 | [*] "Stock" Android. I believe most Android phone tethering should behave 111 | this way 112 | * USBCompositeDevice: 0 / 0 / 0 113 | - InterfaceAssociation[2] 224 / 1 / 3 114 | - ControlInterface: 224 / 1 / 3 115 | - DataInterface: 10 / 0 / 0 116 | * Info.plist entry: RNDISControlStockAndroid(interface) 117 | 118 | [*] Linux USB Gadget drivers. Location: 119 | /drivers/usb/gadget/function/f_rndis.c 120 | These show up in various embedded Linux boards, such as Beagle Board, 121 | Analog Devices PlutoSDR, etc. 122 | * USBCompositeDevice: 0 / 0 / 0 123 | - InterfaceAssociation[2]: Configurable (e.g. 2/6/0, 239/4/1). 124 | - ControlInterface: 2 / 2 / 255 125 | - DataInterface: 10 / 0 / 0 126 | * Info.plist entry: RNDISControlLinuxGadget(interface) 127 | 128 | [*] Wireless Controller Device (class 224). Some Samsung phones (e.g. S7 Edge) 129 | specify device class 224 for tethering, instead of just being a USB 130 | composite device. The rest is the same as in "stock" Android. 131 | Note, the other Samsung phones (e.g. S8) behave like other Android devices. 132 | * Device: 224 / 0 / 0 133 | - (same as "Stock" Android) 134 | 135 | [*] Composite Device, using 0xEF/4/1 for RNDIS control: Nokia 7 Plus (issue #88) 136 | Also may apply to Sony Xperia XZ. 137 | This matches "RNDIS over Ethernet" specification given here: 138 | http://www.usb.org/developers/defined_class/#BaseClassEFh 139 | * USBCompositeDevice: 0 / 0 / 0 140 | - InterfaceAssociation[2]: 239 / 4 / 1 141 | - ControlInterface: 239 / 4 / 1 142 | - DataInterface: 10 / 0 / 0 143 | * Info.plist entry: RNDISControlMiscDeviceRoE(interface) 144 | */ 145 | 146 | // Detects the 224/1/3 - stock Android RNDIS control interface. 147 | static inline bool isRNDISControlStockAndroid(const InterfaceDescriptor *idesc) { 148 | return idesc->bInterfaceClass == 224 // Wireless Controller 149 | && idesc->bInterfaceSubClass == 1 // Radio Frequency 150 | && idesc->bInterfaceProtocol == 3; // RNDIS protocol 151 | } 152 | 153 | // Miscellaneous Device (0xEF), RNDIS over Ethernet: some phones, see above. 154 | static inline bool isRNDISControlMiscDeviceRoE(const InterfaceDescriptor *idesc) { 155 | return idesc->bInterfaceClass == 239 // Miscellaneous Device 156 | && idesc->bInterfaceSubClass == 4 // RNDIS? 157 | && idesc->bInterfaceProtocol == 1; // RNDIS over Ethernet 158 | } 159 | 160 | // Detects RNDIS control on BeagleBoard and possibly other embedded Linux devices. 161 | static inline bool isRNDISControlLinuxGadget(const InterfaceDescriptor *idesc) { 162 | return idesc->bInterfaceClass == 2 // Communications / CDC Control 163 | && idesc->bInterfaceSubClass == 2 // Abstract (modem) 164 | && idesc->bInterfaceProtocol == 255; // Vendor Specific (RNDIS). 165 | } 166 | 167 | // Any of the above RNDIS control interface. 168 | static inline bool isRNDISControlInterface(const InterfaceDescriptor *idesc) { 169 | return isRNDISControlStockAndroid(idesc) 170 | || isRNDISControlLinuxGadget(idesc) 171 | || isRNDISControlMiscDeviceRoE(idesc); 172 | } 173 | 174 | // Detects the class 10 - CDC data interface. 175 | static inline bool isCDCDataInterface(const InterfaceDescriptor *idesc) { 176 | // Check for CDC class. Sub-class and Protocol are undefined: 177 | return idesc->bInterfaceClass == 10; 178 | } 179 | 180 | bool HoRNDIS::init(OSDictionary *properties) { 181 | extern kmod_info_t kmod_info; // Getting the version from generated file. 182 | LOG(V_NOTE, "HoRNDIS tethering driver for Mac OS X, %s", kmod_info.version); 183 | 184 | if (super::init(properties) == false) { 185 | LOG(V_ERROR, "initialize superclass failed"); 186 | return false; 187 | } 188 | 189 | LOG(V_PTR, "PTR: I am: %p", this); 190 | 191 | fNetworkInterface = NULL; 192 | fpNetStats = NULL; 193 | 194 | fReadyToTransfer = false; 195 | fNetifEnabled = false; 196 | fEnableDisableInProgress = false; 197 | fDataDead = false; 198 | 199 | fProbeConfigVal = 0; 200 | fProbeCommIfNum = 0; 201 | 202 | fCallbackCount = 0; 203 | 204 | fCommInterface = NULL; 205 | fDataInterface = NULL; 206 | 207 | fInPipe = NULL; 208 | fOutPipe = NULL; 209 | 210 | numFreeOutBufs = 0; 211 | for (int i = 0; i < N_OUT_BUFS; i++) { 212 | outbufs[i].mdp = NULL; 213 | outbufStack[i] = i; // Value does not matter here. 214 | } 215 | for (int i = 0 ; i < N_IN_BUFS; i++) { 216 | inbufs[i].mdp = NULL; 217 | } 218 | 219 | rndisXid = 1; 220 | maxOutTransferSize = 0; 221 | 222 | return true; 223 | } 224 | 225 | void HoRNDIS::free() { 226 | // Here, we shall free everything allocated by the 'init'. 227 | 228 | LOG(V_NOTE, "driver instance terminated"); // For the default level 229 | super::free(); 230 | } 231 | 232 | /* 233 | ================================================== 234 | INTERFACE PROLIFERATION AND PROVIDER CLASS NAME 235 | ================================================== 236 | PROBLEM: 237 | Every time you connect the same Android device (or somewhat more rarely), 238 | MacOS creates a new entry under "Network" configurations tab. These entries 239 | keep on coming on and on, polluting the configuration. 240 | 241 | ROOT CAUSE: 242 | Android devices randomly-generate Ethernet MAC address for 243 | RNDIS interface, so the system may think there is a new device 244 | every time you connect an Android phone, and may create a new 245 | network interface every such time. 246 | Luckily, it does extra check when Network Provider is a USB device: 247 | in that case, it would match based on USB data, creating an entry like: 248 | SCNetworkInterfaceInfo 249 | 250 | USB Product Name 251 | Pixel 2 252 | UserDefinedName 253 | Pixel 2 254 | idProduct 255 | 20195 256 | ... 257 | In: /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist 258 | When that works, interfaces do not proliferate (at least in most cases). 259 | 260 | PROBLEM CAUSE: 261 | The MacOS network daemon (or whatever it is) looks at interface's 262 | "IOProviderClass" to see if it's USB Device. Unfortunately, it may not pick 263 | up all the names, e.g. it may trigger off the old "IOUSBDevice", but not 264 | the new "IOUSBHostDevice". This problem is present in El Capitan for both 265 | device and the interface, and seems to be present in later systems for 266 | IOUSBDevice. 267 | 268 | FIX/HACK: 269 | The IOUSBHostDevice and IOUSBHostInterface providers actually specify 270 | the "IOClassNameOverride" that gives the old name. We just take this value 271 | and update our "IOProviderClass" to that. 272 | */ 273 | 274 | bool HoRNDIS::start(IOService *provider) { 275 | LOG(V_DEBUG, ">"); 276 | 277 | // Per comment in "IONetworkController.h", 'super::start' should be the 278 | // first method called in the overridden implementation. It allocates the 279 | // network queue for the interface. The rest of the networking 280 | // initialization will be done by 'createNetworkInterface', once USB 281 | // USB is ready. 282 | if(!super::start(provider)) { 283 | return false; 284 | } 285 | 286 | { // Fixing the Provider class name. 287 | // See "INTERFACE PROLIFERATION AND PROVIDER CLASS NAME" description. 288 | OSObject *providerClass = provider->getProperty("IOClassNameOverride"); 289 | if (providerClass) { 290 | setProperty(kIOProviderClassKey, providerClass); 291 | } 292 | } 293 | 294 | if (!openUSBInterfaces(provider)) { 295 | goto bailout; 296 | } 297 | 298 | if (!rndisInit()) { 299 | goto bailout; 300 | } 301 | 302 | // NOTE: The RNDIS spec mandates the usage of Keep Alive timer; however, 303 | // the Android does not seem to be missing its absense, so there is 304 | // probably no use in implementing it. 305 | 306 | LOG(V_DEBUG, "done with RNDIS initialization: can start network interface"); 307 | 308 | // Let's create the medium tables here, to avoid doing extra 309 | // steps in 'enable'. Also, comments recommend creating medium tables 310 | // in the 'setup' stage. 311 | const IONetworkMedium *primaryMedium; 312 | if (!createMediumTables(&primaryMedium) || 313 | !setCurrentMedium(primaryMedium)) { 314 | goto bailout; 315 | } 316 | 317 | // Looks like everything's good... publish the interface! 318 | if (!createNetworkInterface()) { 319 | goto bailout; 320 | } 321 | 322 | // This call is based on traces of Thunderbolt Ethernet driver. 323 | // That driver calls 'setLinkStatus(0x1)' before interface publish 324 | // callback (which happens after 'start'). 325 | setLinkStatus(kIONetworkLinkValid); 326 | 327 | LOG(V_DEBUG, "successful"); 328 | return true; 329 | 330 | bailout: 331 | stop(provider); 332 | return false; 333 | } 334 | 335 | bool HoRNDIS::willTerminate(IOService *provider, IOOptionBits options) { 336 | LOG(V_DEBUG, ">"); 337 | // The 'willTerminate' is called when USB device disappears - the user 338 | // either disconnected the USB, or switched-off tethering. It's likely 339 | // that the pending read has already invoked a callback with unreachable 340 | // device or aborted status, and already terminated. If not, closing of 341 | // the USB Data interface would force it to abort. 342 | // 343 | // Note, per comments in 'IOUSBHostInterface.h' (for some later version 344 | // of MacOS SDK), this is the recommended place to close USB interfaces. 345 | // 346 | // This happens before ::stop, but after some of the read jobs fail 347 | // with kIOReturnNotResponding (and some of the writers might fail, 348 | // too). ::disable happens sometime after we get done here, too -- 349 | // potentially invoked by super::willTerminate. 350 | 351 | disableNetworkQueue(); 352 | closeUSBInterfaces(); 353 | 354 | return super::willTerminate(provider, options); 355 | } 356 | 357 | void HoRNDIS::stop(IOService *provider) { 358 | LOG(V_DEBUG, ">"); 359 | 360 | OSSafeReleaseNULL(fNetworkInterface); 361 | 362 | closeUSBInterfaces(); // Just in case - supposed to be closed by now. 363 | 364 | super::stop(provider); 365 | } 366 | 367 | 368 | // Convenience function: to retain and assign in one step: 369 | template static inline T *retainT(T *ptr) { 370 | ptr->retain(); 371 | return ptr; 372 | } 373 | 374 | bool HoRNDIS::openUSBInterfaces(IOService *provider) { 375 | if (fProbeConfigVal == 0) { 376 | // Must have been set by 'probe' before 'start' function call: 377 | LOG(V_ERROR, "'fProbeConfigVal' has not been set, bailing out"); 378 | return false; 379 | } 380 | 381 | IOUSBHostDevice *device = OSDynamicCast(IOUSBHostDevice, provider); 382 | if (device) { 383 | // Set the device configuration, so we can start looking at the interfaces: 384 | if (device->setConfiguration(fProbeConfigVal, false) != kIOReturnSuccess) { 385 | LOG(V_ERROR, "Cannot set the USB Device configuration"); 386 | return false; 387 | } 388 | } else { 389 | IOUSBHostInterface *iface = OSDynamicCast(IOUSBHostInterface, provider); 390 | if (iface == NULL) { 391 | LOG(V_ERROR, "start: BUG unexpected provider class"); 392 | return false; 393 | } 394 | device = iface->getDevice(); 395 | // Make sure it's the one we care about: 396 | bool match = iface->getConfigurationDescriptor()->bConfigurationValue == fProbeConfigVal 397 | && iface->getInterfaceDescriptor()->bInterfaceNumber == fProbeCommIfNum; 398 | if (!match) { 399 | LOG(V_ERROR, "BUG! Did we see a different provider in probe?"); 400 | return false; 401 | } 402 | } 403 | 404 | { // Now, find the interfaces: 405 | OSIterator *iterator = device->getChildIterator(gIOServicePlane); 406 | OSObject *obj = NULL; 407 | while(iterator != NULL && (obj = iterator->getNextObject()) != NULL) { 408 | IOUSBHostInterface *iface = OSDynamicCast(IOUSBHostInterface, obj); 409 | if (iface == NULL) { 410 | continue; 411 | } 412 | if (iface->getConfigurationDescriptor()->bConfigurationValue != 413 | fProbeConfigVal) { 414 | continue; 415 | } 416 | const InterfaceDescriptor *desc = iface->getInterfaceDescriptor(); 417 | uint8_t ifaceNum = desc->bInterfaceNumber; 418 | if (!fCommInterface && ifaceNum == fProbeCommIfNum) { 419 | LOG(V_DEBUG, "Found control interface: %d/%d/%d, opening", 420 | desc->bInterfaceClass, desc->bInterfaceSubClass, 421 | desc->bInterfaceProtocol); 422 | if (!iface->open(this)) { 423 | LOG(V_ERROR, "Could not open RNDIS control interface"); 424 | return false; 425 | } 426 | // Note, we retain AFTER opening the interface, because once 427 | // 'fCommInterface' is set, the 'closeUSBInterfaces' would 428 | // always try to close it before releasing: 429 | fCommInterface = retainT(iface); 430 | } else if (ifaceNum == fProbeCommIfNum + 1) { 431 | LOG(V_DEBUG, "Found data interface: %d/%d/%d, opening", 432 | desc->bInterfaceClass, desc->bInterfaceSubClass, 433 | desc->bInterfaceProtocol); 434 | if (!iface->open(this)) { 435 | LOG(V_ERROR, "Could not open RNDIS data interface"); 436 | return false; 437 | } 438 | // open before retain, see above: 439 | fDataInterface = retainT(iface); 440 | break; // We should be done by now. 441 | } 442 | } 443 | OSSafeReleaseNULL(iterator); 444 | } 445 | 446 | // WARNING, it is a WRONG idea to attach 'fDataInterface' as a second 447 | // provider, because both providers would be calling 'willTerminate', and 448 | // 'stop' methods, resulting in chaos. 449 | 450 | if (!fCommInterface || !fDataInterface) { 451 | LOG(V_ERROR, "could not find the required interfaces, despite seeing " 452 | "their descriptors during 'probe' method call"); 453 | return false; 454 | } 455 | 456 | { // Get the pipes for the data interface: 457 | const EndpointDescriptor *candidate = NULL; 458 | const InterfaceDescriptor *intDesc = fDataInterface->getInterfaceDescriptor(); 459 | const ConfigurationDescriptor *confDesc = fDataInterface->getConfigurationDescriptor(); 460 | if (intDesc->bNumEndpoints != 2) { 461 | LOG(V_ERROR, "Expected 2 endpoints for Data Interface, got: %d", intDesc->bNumEndpoints); 462 | return false; 463 | } 464 | while((candidate = StandardUSB::getNextEndpointDescriptor( 465 | confDesc, intDesc, candidate)) != NULL) { 466 | const bool isEPIn = 467 | (candidate->bEndpointAddress & kEndpointDescriptorDirection) != 0; 468 | IOUSBHostPipe *&pipe = isEPIn ? fInPipe : fOutPipe; 469 | if (pipe == NULL) { 470 | // Note, 'copyPipe' already performs 'retain': must not call it again. 471 | pipe = fDataInterface->copyPipe(candidate->bEndpointAddress); 472 | } 473 | } 474 | if (fInPipe == NULL || fOutPipe == NULL) { 475 | LOG(V_ERROR, "Could not init IN/OUT pipes in the Data Interface"); 476 | return false; 477 | } 478 | } 479 | 480 | return true; 481 | } 482 | 483 | void HoRNDIS::closeUSBInterfaces() { 484 | fReadyToTransfer = false; // Interfaces are about to be closed. 485 | // Close the interfaces - this would abort the transfers (if present): 486 | if (fDataInterface) { 487 | fDataInterface->close(this); 488 | } 489 | if (fCommInterface) { 490 | fCommInterface->close(this); 491 | } 492 | 493 | OSSafeReleaseNULL(fInPipe); 494 | OSSafeReleaseNULL(fOutPipe); 495 | OSSafeReleaseNULL(fDataInterface); 496 | OSSafeReleaseNULL(fCommInterface); // First one to open, last one to die. 497 | } 498 | 499 | IOService *HoRNDIS::probe(IOService *provider, SInt32 *score) { 500 | LOG(V_DEBUG, "came in with a score of %d", *score); 501 | { // Check if this is a device-based matching: 502 | IOUSBHostDevice *device = OSDynamicCast(IOUSBHostDevice, provider); 503 | if (device) { 504 | return probeDevice(device, score); 505 | } 506 | } 507 | 508 | IOUSBHostInterface *controlIf = OSDynamicCast(IOUSBHostInterface, provider); 509 | if (controlIf == NULL) { 510 | LOG(V_ERROR, "unexpected provider class (wrong Info.plist)"); 511 | return NULL; 512 | } 513 | 514 | const InterfaceDescriptor *desc = controlIf->getInterfaceDescriptor(); 515 | LOG(V_DEBUG, "Interface-based matching, probing for device '%s', " 516 | "interface %d/%d/%d", controlIf->getDevice()->getName(), 517 | desc->bInterfaceClass, desc->bInterfaceSubClass, 518 | desc->bInterfaceProtocol); 519 | if (!isRNDISControlInterface(controlIf->getInterfaceDescriptor())) { 520 | LOG(V_ERROR, "not RNDIS control interface (wrong Info.plist)"); 521 | return NULL; 522 | } 523 | 524 | const ConfigurationDescriptor *configDesc = 525 | controlIf->getConfigurationDescriptor(); 526 | const InterfaceDescriptor *dataDesc = 527 | StandardUSB::getNextInterfaceDescriptor(configDesc, desc); 528 | bool match = isCDCDataInterface(dataDesc) && 529 | (dataDesc->bInterfaceNumber == desc->bInterfaceNumber + 1); 530 | if (!match) { 531 | LOG(V_DEBUG, "Could not find CDC data interface right after control"); 532 | return NULL; 533 | } 534 | fProbeConfigVal = configDesc->bConfigurationValue; 535 | fProbeCommIfNum = desc->bInterfaceNumber; 536 | *score += 100000; 537 | return this; 538 | } 539 | 540 | IOService *HoRNDIS::probeDevice(IOUSBHostDevice *device, SInt32 *score) { 541 | const DeviceDescriptor *desc = device->getDeviceDescriptor(); 542 | LOG(V_DEBUG, "Device-based matching, probing: '%s', %d/%d/%d", 543 | device->getName(), desc->bDeviceClass, desc->bDeviceSubClass, 544 | desc->bDeviceProtocol); 545 | // Look through all configurations and find the one we want: 546 | for (int i = 0; i < desc->bNumConfigurations; i++) { 547 | const ConfigurationDescriptor *configDesc = 548 | device->getConfigurationDescriptor(i); 549 | if (configDesc == NULL) { 550 | LOG(V_ERROR, "Cannot get device's configuration descriptor"); 551 | return NULL; 552 | } 553 | int controlIfNum = INT16_MAX; // Definitely invalid interface number. 554 | bool foundData = false; 555 | const InterfaceDescriptor *intDesc = NULL; 556 | while((intDesc = StandardUSB::getNextInterfaceDescriptor(configDesc, intDesc)) != NULL) { 557 | // If this is a device-level match, check for the control interface: 558 | if (isRNDISControlInterface(intDesc)) { // Just check them all. 559 | controlIfNum = intDesc->bInterfaceNumber; 560 | continue; 561 | } 562 | 563 | // We check for data interface AND make sure it follows directly the 564 | // control interface. Note the condition below would only trigger 565 | // if we previously found an appropriate 'controlIfNum': 566 | if (isCDCDataInterface(intDesc) && 567 | intDesc->bInterfaceNumber == controlIfNum + 1) { 568 | foundData = true; 569 | break; 570 | } 571 | } 572 | if (foundData) { 573 | // We've found it! Save the information and return: 574 | fProbeConfigVal = configDesc->bConfigurationValue; 575 | fProbeCommIfNum = controlIfNum; 576 | *score += 10000; 577 | return this; 578 | } 579 | } 580 | 581 | // Did not find any interfaces we can use: 582 | LOG(V_DEBUG, "The device '%s' does not contain the required interfaces: " 583 | "it is not for us", device->getName()); 584 | return NULL; 585 | } 586 | 587 | /***** Ethernet interface bits *****/ 588 | 589 | /* We need our own createInterface (overriding the one in IOEthernetController) 590 | * because we need our own subclass of IOEthernetInterface. Why's that, you say? 591 | * Well, we need that because that's the only way to set a different default MTU, 592 | * because it seems like MacOS code just assumes that any EthernetController 593 | * driver must be able to handle al leaset 1500-byte Ethernet payload. 594 | * Sigh... 595 | * The MTU-limiting code may never come into play though, because the devices 596 | * I've seen have "max_transfer_size" large enough to accomodate a max-length 597 | * Ethernet frames. */ 598 | 599 | bool HoRNDISInterface::init(IONetworkController *controller, int mtu) { 600 | maxmtu = mtu; 601 | if (IOEthernetInterface::init(controller) == false) { 602 | return false; 603 | } 604 | LOG(V_NOTE, "(network interface) starting up with MTU %d", mtu); 605 | setMaxTransferUnit(mtu); 606 | return true; 607 | } 608 | 609 | bool HoRNDISInterface::setMaxTransferUnit(UInt32 mtu) { 610 | if (mtu > maxmtu) { 611 | LOG(V_NOTE, "Excuse me, but I said you could have an MTU of %u, and you just tried to set an MTU of %d. Good try, buddy.", maxmtu, mtu); 612 | return false; 613 | } 614 | IOEthernetInterface::setMaxTransferUnit(mtu); 615 | return true; 616 | } 617 | 618 | /* Overrides IOEthernetController::createInterface */ 619 | IONetworkInterface *HoRNDIS::createInterface() { 620 | LOG(V_DEBUG, ">"); 621 | HoRNDISInterface *netif = new HoRNDISInterface; 622 | 623 | if (!netif) { 624 | return NULL; 625 | } 626 | 627 | int mtuLimit = maxOutTransferSize 628 | - (int)sizeof(rndis_data_hdr) 629 | - 14; // Size of ethernet header (no QLANs). Checksum is not included. 630 | 631 | if (!netif->init(this, min(ETHERNET_MTU, mtuLimit))) { 632 | netif->release(); 633 | return NULL; 634 | } 635 | 636 | return netif; 637 | } 638 | 639 | bool HoRNDIS::createNetworkInterface() { 640 | LOG(V_DEBUG, "attaching and registering interface"); 641 | 642 | // MTU is initialized before we get here, so this is a safe time to do this. 643 | if (!attachInterface((IONetworkInterface **)&fNetworkInterface, true)) { 644 | LOG(V_ERROR, "attachInterface failed?"); 645 | return false; 646 | } 647 | LOG(V_PTR, "fNetworkInterface: %p", fNetworkInterface); 648 | 649 | // The 'registerService' should be called by 'attachInterface' (with second 650 | // parameter set to true). No need to do it here. 651 | 652 | return true; 653 | } 654 | 655 | /***** Interface enable and disable logic *****/ 656 | HoRNDIS::ReentryLocker::ReentryLocker(IOCommandGate *inGate, bool &inGuard) 657 | : gate(inGate), entryGuard(inGuard), result(kIOReturnSuccess) { 658 | // Note, please see header comment for the motivation behind 659 | // 'ReentryLocker' and its high-level functionality. 660 | // Wait until we exit the previously-entered enable or disable method: 661 | while (entryGuard) { 662 | LOG(V_DEBUG, "Delaying the re-entered call"); 663 | result = gate->commandSleep(&entryGuard); 664 | // If "commandSleep" has failed, stop immediately, and don't 665 | // touch the 'entryGuard': 666 | if (isInterrupted()) { 667 | return; 668 | } 669 | } 670 | entryGuard = true; // Mark the entry into one of the protected methods. 671 | } 672 | 673 | HoRNDIS::ReentryLocker::~ReentryLocker() { 674 | if (!isInterrupted()) { 675 | entryGuard = false; 676 | gate->commandWakeup(&entryGuard); 677 | } 678 | } 679 | 680 | static IOReturn loopClearPipeStall(IOUSBHostPipe *pipe) { 681 | IOReturn rc = kUSBHostReturnPipeStalled; 682 | int count = 0; 683 | // For some reason, 'clearStall' may keep on returning 684 | // kUSBHostReturnPipeStalled many times, before finally returning success 685 | // (Android keeps on sending packtes, each generating a stall?). 686 | const int NUM_RETRIES = 1000; 687 | for (; count < NUM_RETRIES && rc == kUSBHostReturnPipeStalled; count++) { 688 | rc = pipe->clearStall(true); 689 | } 690 | LOG(V_DEBUG, "Called 'clearStall' %d times", count); 691 | return rc; 692 | } 693 | 694 | /*! 695 | * Calls 'pipe->io', and if there is a stall, tries to clear that 696 | * stall and calls it again. 697 | */ 698 | static inline IOReturn robustIO(IOUSBHostPipe *pipe, pipebuf_t *buf, 699 | uint32_t len) { 700 | IOReturn rc = pipe->io(buf->mdp, len, &buf->comp); 701 | if (rc == kUSBHostReturnPipeStalled) { 702 | LOG(V_DEBUG, "USB Pipe is stalled. Trying to clear ..."); 703 | rc = loopClearPipeStall(pipe); 704 | // If clearing the stall succeeded, try the IO operation again: 705 | if (rc == kIOReturnSuccess) { 706 | LOG(V_DEBUG, "Cleared USB Stall, Retrying the operation"); 707 | rc = pipe->io(buf->mdp, len, &buf->comp); 708 | } 709 | } 710 | return rc; 711 | } 712 | 713 | /* Contains buffer alloc and dealloc, notably. Why do that here? 714 | Not just because that's what Apple did. We don't want to consume these 715 | resources when the interface is sitting disabled and unused. */ 716 | IOReturn HoRNDIS::enable(IONetworkInterface *netif) { 717 | IOReturn rtn = kIOReturnSuccess; 718 | 719 | LOG(V_DEBUG, "begin for thread_id=%lld", thread_tid(current_thread())); 720 | ReentryLocker locker(this, fEnableDisableInProgress); 721 | if (locker.isInterrupted()) { 722 | LOG(V_ERROR, "Waiting interrupted"); 723 | return locker.getResult(); 724 | } 725 | 726 | if (fNetifEnabled) { 727 | LOG(V_DEBUG, "Repeated call (thread_id=%lld), returning success", 728 | thread_tid(current_thread())); 729 | return kIOReturnSuccess; 730 | } 731 | 732 | if (fCallbackCount != 0) { 733 | LOG(V_ERROR, "Invalid state: fCallbackCount(=%d) != 0", fCallbackCount); 734 | return kIOReturnError; 735 | } 736 | 737 | if (!allocateResources()) { 738 | return kIOReturnNoMemory; 739 | } 740 | 741 | // Tell the other end to start transmitting. 742 | if (!rndisSetPacketFilter(RNDIS_DEFAULT_FILTER)) { 743 | goto bailout; 744 | } 745 | 746 | // The pipe stall clearning is not needed for the first "enable" call after 747 | // pugging in the device, but it becomes necessary when "disable" is called 748 | // after that, followed by another "enable". This happens when user runs 749 | // "sudo ifconfig down", followed by "sudo ifconfig up" 750 | LOG(V_DEBUG, "Clearing potential Pipe stalls on Input and Output pipes"); 751 | loopClearPipeStall(fInPipe); 752 | loopClearPipeStall(fOutPipe); 753 | 754 | // We can now perform reads and writes between Network stack and USB device: 755 | fReadyToTransfer = true; 756 | 757 | // Kick off the read requests: 758 | for (int i = 0; i < N_IN_BUFS; i++) { 759 | pipebuf_t &inbuf = inbufs[i]; 760 | inbuf.comp.owner = this; 761 | inbuf.comp.action = dataReadComplete; 762 | inbuf.comp.parameter = &inbuf; 763 | 764 | rtn = robustIO(fInPipe, &inbuf, (uint32_t)inbuf.mdp->getLength()); 765 | if (rtn != kIOReturnSuccess) { 766 | LOG(V_ERROR, "Failed to start the first read: %08x\n", rtn); 767 | goto bailout; 768 | } 769 | fCallbackCount++; 770 | } 771 | 772 | // Tell the world that the link is up... 773 | if (!setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, 774 | getCurrentMedium())) { 775 | LOG(V_ERROR, "Cannot set link status"); 776 | rtn = kIOReturnError; 777 | goto bailout; 778 | } 779 | 780 | // ... and then listen for packets! 781 | getOutputQueue()->setCapacity(TRANSMIT_QUEUE_SIZE); 782 | getOutputQueue()->start(); 783 | LOG(V_DEBUG, "txqueue started"); 784 | 785 | // Now we can say we're alive. 786 | fNetifEnabled = true; 787 | LOG(V_NOTE, "completed (thread_id=%lld): RNDIS network interface '%s' " 788 | "should be live now", thread_tid(current_thread()), netif->getName()); 789 | 790 | return kIOReturnSuccess; 791 | 792 | bailout: 793 | disableImpl(); 794 | return rtn; 795 | } 796 | 797 | void HoRNDIS::disableNetworkQueue() { 798 | // Disable the queue (no more outputPacket), 799 | // and then flush everything in the queue. 800 | getOutputQueue()->stop(); 801 | getOutputQueue()->flush(); 802 | getOutputQueue()->setCapacity(0); 803 | } 804 | 805 | IOReturn HoRNDIS::disable(IONetworkInterface *netif) { 806 | LOG(V_DEBUG, "begin for thread_id=%lld", thread_tid(current_thread())); 807 | // This function can be called as a consequence of: 808 | // 1. USB Disconnect 809 | // 2. Some action, while the device is up and running 810 | // (e.g. "ifconfig en6 down"). 811 | // In the second case, we'll need to do more cleanup: 812 | // ask the RNDIS device to stop transmitting, and abort the callbacks. 813 | // 814 | 815 | ReentryLocker locker(this, fEnableDisableInProgress); 816 | if (locker.isInterrupted()) { 817 | LOG(V_ERROR, "Waiting interrupted"); 818 | return locker.getResult(); 819 | } 820 | 821 | if (!fNetifEnabled) { 822 | LOG(V_DEBUG, "Repeated call (thread_id=%lld)", thread_tid(current_thread())); 823 | return kIOReturnSuccess; 824 | } 825 | 826 | disableImpl(); 827 | 828 | LOG(V_DEBUG, "completed (thread_id=%lld)", thread_tid(current_thread())); 829 | return kIOReturnSuccess; 830 | } 831 | 832 | void HoRNDIS::disableImpl() { 833 | disableNetworkQueue(); 834 | 835 | // Stop the the new transfers. The code below would cancel the pending ones: 836 | fReadyToTransfer = false; 837 | 838 | // If the device has not been disconnected, ask it to stop xmitting: 839 | if (fCommInterface) { 840 | rndisSetPacketFilter(0); 841 | } 842 | 843 | // Again, based on Thunderbolt Ethernet controller traces. 844 | // It sets the link status to 0x1 in the disable call: 845 | setLinkStatus(kIONetworkLinkValid, 0); 846 | 847 | // If USB interfaces are still up, abort the reader and writer: 848 | if (fInPipe) { 849 | fInPipe->abort(IOUSBHostIOSource::kAbortSynchronous, 850 | kIOReturnAborted, NULL); 851 | } 852 | if (fOutPipe) { 853 | fOutPipe->abort(IOUSBHostIOSource::kAbortSynchronous, 854 | kIOReturnAborted, NULL); 855 | } 856 | // Make sure all the callbacks have exited: 857 | LOG(V_DEBUG, "Callback count: %d. If not zero, delaying ...", 858 | fCallbackCount); 859 | while (fCallbackCount > 0) { 860 | // No timeout: in our callbacks we trust! 861 | getCommandGate()->commandSleep(&fCallbackCount); 862 | } 863 | LOG(V_DEBUG, "All callbacks exited"); 864 | 865 | // Release all resources 866 | releaseResources(); 867 | 868 | fNetifEnabled = false; 869 | } 870 | 871 | bool HoRNDIS::createMediumTables(const IONetworkMedium **primary) { 872 | IONetworkMedium *medium; 873 | 874 | OSDictionary *mediumDict = OSDictionary::withCapacity(1); 875 | if (mediumDict == NULL) { 876 | LOG(V_ERROR, "Cannot allocate OSDictionary"); 877 | return false; 878 | } 879 | 880 | medium = IONetworkMedium::medium(kIOMediumEthernetAuto, 480 * 1000000); 881 | IONetworkMedium::addMedium(mediumDict, medium); 882 | medium->release(); // 'mediumDict' holds a ref now. 883 | if (primary) { 884 | *primary = medium; 885 | } 886 | 887 | bool result = publishMediumDictionary(mediumDict); 888 | if (!result) { 889 | LOG(V_ERROR, "Cannot publish medium dictionary!"); 890 | } 891 | 892 | // Per comment for 'publishMediumDictionary' in NetworkController.h, the 893 | // medium dictionary is copied and may be safely relseased after the call. 894 | mediumDict->release(); 895 | 896 | return result; 897 | } 898 | 899 | bool HoRNDIS::allocateResources() { 900 | LOG(V_DEBUG, "Allocating %d input buffers (size=%d) and %d output " 901 | "buffers (size=%d)", N_IN_BUFS, IN_BUF_SIZE, N_OUT_BUFS, OUT_BUF_SIZE); 902 | 903 | // Grab a memory descriptor pointer for data-in. 904 | for (int i = 0; i < N_IN_BUFS; i++) { 905 | inbufs[i].mdp = IOBufferMemoryDescriptor::withCapacity(IN_BUF_SIZE, kIODirectionIn); 906 | if (!inbufs[i].mdp) { 907 | return false; 908 | } 909 | inbufs[i].mdp->setLength(IN_BUF_SIZE); 910 | LOG(V_PTR, "PTR: inbuf[%d].mdp: %p", i, inbufs[i].mdp); 911 | } 912 | 913 | // And a handful for data-out... 914 | for (int i = 0; i < N_OUT_BUFS; i++) { 915 | outbufs[i].mdp = IOBufferMemoryDescriptor::withCapacity( 916 | OUT_BUF_SIZE, kIODirectionOut); 917 | if (!outbufs[i].mdp) { 918 | LOG(V_ERROR, "allocate output descriptor failed"); 919 | return false; 920 | } 921 | LOG(V_PTR, "PTR: outbufs[%d].mdp: %p", i, outbufs[i].mdp); 922 | outbufs[i].mdp->setLength(OUT_BUF_SIZE); 923 | outbufStack[i] = i; 924 | } 925 | numFreeOutBufs = N_OUT_BUFS; 926 | 927 | return true; 928 | } 929 | 930 | void HoRNDIS::releaseResources() { 931 | LOG(V_DEBUG, "releaseResources"); 932 | 933 | fReadyToTransfer = false; // No transfers without buffers. 934 | for (int i = 0; i < N_OUT_BUFS; i++) { 935 | OSSafeReleaseNULL(outbufs[i].mdp); 936 | outbufStack[i] = i; 937 | } 938 | numFreeOutBufs = 0; 939 | 940 | for (int i = 0; i < N_IN_BUFS; i++) { 941 | OSSafeReleaseNULL(inbufs[i].mdp); 942 | } 943 | } 944 | 945 | IOOutputQueue *HoRNDIS::createOutputQueue() { 946 | LOG(V_DEBUG, ">"); 947 | // The gated Output Queue keeps things simple: everything is 948 | // serialized, no need to worry about locks or concurrency. 949 | // The device is not very fast, so the serial execution should be more 950 | // than capable of keeping up. 951 | // Note, if we ever switch to non-gated queue, we shall update the 952 | // 'outputPacket' to access the shared state using locks + update all the 953 | // other users of that state + may want to use locks for USB calls as well. 954 | return IOGatedOutputQueue::withTarget(this, 955 | getWorkLoop(), TRANSMIT_QUEUE_SIZE); 956 | } 957 | 958 | bool HoRNDIS::configureInterface(IONetworkInterface *netif) { 959 | LOG(V_DEBUG, ">"); 960 | IONetworkData *nd; 961 | 962 | if (super::configureInterface(netif) == false) { 963 | LOG(V_ERROR, "super failed"); 964 | return false; 965 | } 966 | 967 | nd = netif->getNetworkData(kIONetworkStatsKey); 968 | if (!nd || !(fpNetStats = (IONetworkStats *)nd->getBuffer())) { 969 | LOG(V_ERROR, "network statistics buffer unavailable?"); 970 | return false; 971 | } 972 | 973 | LOG(V_PTR, "fpNetStats: %p", fpNetStats); 974 | 975 | return true; 976 | } 977 | 978 | 979 | /***** All-purpose IOKit network routines *****/ 980 | 981 | IOReturn HoRNDIS::getPacketFilters(const OSSymbol *group, UInt32 *filters) const { 982 | IOReturn rtn = kIOReturnSuccess; 983 | 984 | if (group == gIOEthernetWakeOnLANFilterGroup) { 985 | *filters = 0; 986 | } else if (group == gIONetworkFilterGroup) { 987 | *filters = kIOPacketFilterUnicast | kIOPacketFilterBroadcast 988 | | kIOPacketFilterPromiscuous | kIOPacketFilterMulticast 989 | | kIOPacketFilterMulticastAll; 990 | } else { 991 | rtn = super::getPacketFilters(group, filters); 992 | } 993 | 994 | return rtn; 995 | } 996 | 997 | IOReturn HoRNDIS::getMaxPacketSize(UInt32 *maxSize) const { 998 | IOReturn rc = super::getMaxPacketSize(maxSize); 999 | if (rc != kIOReturnSuccess) { 1000 | return rc; 1001 | } 1002 | // The max packet size is limited by RNDIS max transfer size: 1003 | *maxSize = min(*maxSize, maxOutTransferSize - sizeof(rndis_data_hdr)); 1004 | LOG(V_DEBUG, "returning %d", *maxSize); 1005 | return kIOReturnSuccess; 1006 | } 1007 | 1008 | IOReturn HoRNDIS::selectMedium(const IONetworkMedium *medium) { 1009 | LOG(V_DEBUG, ">"); 1010 | setSelectedMedium(medium); 1011 | 1012 | return kIOReturnSuccess; 1013 | } 1014 | 1015 | IOReturn HoRNDIS::getHardwareAddress(IOEthernetAddress *ea) { 1016 | LOG(V_DEBUG, ">"); 1017 | UInt32 i; 1018 | void *buf; 1019 | unsigned char *bp; 1020 | int rlen = -1; 1021 | int rv; 1022 | 1023 | buf = IOMallocAligned(RNDIS_CMD_BUF_SZ, sizeof(void *)); 1024 | if (!buf) { 1025 | return kIOReturnNoMemory; 1026 | } 1027 | 1028 | // WARNING: Android devices may randomly-generate RNDIS MAC address. 1029 | // The function may return different results for the same device. 1030 | 1031 | rv = rndisQuery(buf, OID_802_3_PERMANENT_ADDRESS, 48, (void **) &bp, &rlen); 1032 | if (rv < 0) { 1033 | LOG(V_ERROR, "getHardwareAddress OID failed?"); 1034 | IOFreeAligned(buf, RNDIS_CMD_BUF_SZ); 1035 | return kIOReturnIOError; 1036 | } 1037 | LOG(V_DEBUG, "MAC Address %02x:%02x:%02x:%02x:%02x:%02x -- rlen %d", 1038 | bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], 1039 | rlen); 1040 | 1041 | for (i=0; i<6; i++) { 1042 | ea->bytes[i] = bp[i]; 1043 | } 1044 | 1045 | IOFreeAligned(buf, RNDIS_CMD_BUF_SZ); 1046 | return kIOReturnSuccess; 1047 | } 1048 | 1049 | IOReturn HoRNDIS::setMulticastMode(bool active) { 1050 | // For 'real' RNDIS devices, this should toggle 1051 | // RNDIS_PACKET_TYPE_ALL_MULTICAST or RNDIS_PACKET_TYPE_MULTICAST 1052 | // via 'rndisSetPacketFilter', but Android/Linux kernel 1053 | // doesn't care, so why should we? 1054 | return kIOReturnSuccess; 1055 | } 1056 | 1057 | IOReturn HoRNDIS::setMulticastList(IOEthernetAddress *addrs, 1058 | UInt32 count) { 1059 | // "Honey Badger don't care". We're using MULTICAST_ALL flag: everything 1060 | // gets passed through. 1061 | return kIOReturnSuccess; 1062 | } 1063 | 1064 | IOReturn HoRNDIS::setPromiscuousMode(bool active) { 1065 | // Similar to 'setMulticastMode'. 1066 | // XXX This actually needs to get passed down to support 'real' 1067 | // RNDIS devices, but it will work okay for Android devices. 1068 | return kIOReturnSuccess; 1069 | } 1070 | 1071 | /***** Packet transmit logic *****/ 1072 | 1073 | static inline bool isTransferStopStatus(IOReturn rc) { 1074 | // IOReturn indicating that we need to stop transfers: 1075 | return rc == kIOReturnAborted || rc == kIOReturnNotResponding; 1076 | } 1077 | 1078 | UInt32 HoRNDIS::outputPacket(mbuf_t packet, void *param) { 1079 | IOReturn ior = kIOReturnSuccess; 1080 | int poolIndx = N_OUT_BUFS; 1081 | 1082 | // Note, this function MAY or MAY NOT be protected by the IOCommandGate, 1083 | // depending on the kind of OutputQueue used. 1084 | // Here, we assume that IOCommandGate is used: no need to lock. 1085 | 1086 | if (!fReadyToTransfer) { 1087 | // Technically, we must never be here, because we always disable the 1088 | // queue before clearing 'fReadyToTransfer', but double-checking here 1089 | // just in-case: better safe than sorry. 1090 | LOG(V_DEBUG, "fReadyToTransfer=false: dropping packet " 1091 | "(we shouldn't even be here)"); 1092 | freePacket(packet); 1093 | return kIOReturnOutputDropped; 1094 | } 1095 | 1096 | // Count the total size of this packet 1097 | size_t pktlen = 0; 1098 | for (mbuf_t m = packet; m; m = mbuf_next(m)) { 1099 | pktlen += mbuf_len(m); 1100 | } 1101 | 1102 | LOG(V_PACKET, "%ld bytes", pktlen); 1103 | 1104 | const uint32_t transmitLength = (uint32_t)(pktlen + sizeof(rndis_data_hdr)); 1105 | 1106 | if (transmitLength > maxOutTransferSize) { 1107 | LOG(V_ERROR, "packet too large (%ld bytes, maximum can transmit %ld)", 1108 | pktlen, maxOutTransferSize - sizeof(rndis_data_hdr)); 1109 | fpNetStats->outputErrors++; 1110 | freePacket(packet); 1111 | return kIOReturnOutputDropped; 1112 | } 1113 | 1114 | if (numFreeOutBufs <= 0) { 1115 | LOG(V_ERROR, "BUG: Ran out of buffers - stall did not work!"); 1116 | // Stall the queue and re-try the same packet later: don't release: 1117 | return kIOOutputStatusRetry | kIOOutputCommandStall; 1118 | } 1119 | 1120 | // Note, we don't decrement 'numFreeOutBufs' (commit to using that buffer) 1121 | // until everything is successful. 1122 | poolIndx = outbufStack[numFreeOutBufs - 1]; 1123 | if (poolIndx < 0 || poolIndx >= N_OUT_BUFS) { 1124 | LOG(V_ERROR, "BUG: poolIndex out-of-bounds"); 1125 | freePacket(packet); 1126 | return kIOReturnOutputDropped; 1127 | } 1128 | 1129 | // Start filling in the send buffer 1130 | struct rndis_data_hdr *hdr; 1131 | hdr = (struct rndis_data_hdr *)outbufs[poolIndx].mdp->getBytesNoCopy(); 1132 | 1133 | outbufs[poolIndx].mdp->setLength(transmitLength); 1134 | 1135 | memset(hdr, 0, sizeof *hdr); 1136 | hdr->msg_type = RNDIS_MSG_PACKET; 1137 | hdr->msg_len = cpu_to_le32(pktlen + sizeof *hdr); 1138 | hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8); 1139 | hdr->data_len = cpu_to_le32(pktlen); 1140 | mbuf_copydata(packet, 0, pktlen, hdr + 1); 1141 | 1142 | freePacket(packet); 1143 | packet = NULL; 1144 | 1145 | // Now, fire it off! 1146 | IOUSBHostCompletion *const comp = &outbufs[poolIndx].comp; 1147 | comp->owner = this; 1148 | comp->parameter = (void *)(uintptr_t)poolIndx; 1149 | comp->action = dataWriteComplete; 1150 | 1151 | ior = robustIO(fOutPipe, &outbufs[poolIndx], transmitLength); 1152 | if (ior != kIOReturnSuccess) { 1153 | if (isTransferStopStatus(ior)) { 1154 | LOG(V_DEBUG, "WRITER: The device was possibly disconnected: ignoring the error"); 1155 | } else { 1156 | LOG(V_ERROR, "write failed: %08x", ior); 1157 | fpNetStats->outputErrors++; 1158 | } 1159 | // Packet was already freed: just quit: 1160 | return kIOReturnOutputDropped; 1161 | } 1162 | // Only here - when 'fOutPipe->io' has fired - we mark the buffer in-use: 1163 | numFreeOutBufs--; 1164 | fCallbackCount++; 1165 | fpNetStats->outputPackets++; 1166 | // If we ran out of free buffers, issue a stall command to the queue. 1167 | // Note, this would be "we accept this packet, but don't give us more yet", 1168 | // which is NOT the same as 'kIOReturnOutputStall'. 1169 | const bool stallQueue = (numFreeOutBufs == 0); 1170 | if (stallQueue) { 1171 | LOG(V_PACKET, "Issuing stall command to the output queue"); 1172 | } 1173 | return kIOOutputStatusAccepted | 1174 | (stallQueue ? kIOOutputCommandStall : kIOOutputCommandNone); 1175 | } 1176 | 1177 | void HoRNDIS::callbackExit() { 1178 | fCallbackCount--; 1179 | // Notify the 'disable' that may be waiting for callback count to reach 0: 1180 | if (fCallbackCount <= 0) { 1181 | LOG(V_DEBUG, "Notifying last callback exited"); 1182 | getCommandGate()->commandWakeup(&fCallbackCount); 1183 | } 1184 | } 1185 | 1186 | void HoRNDIS::dataWriteComplete(void *obj, void *param, IOReturn rc, UInt32 transferred) { 1187 | HoRNDIS *me = (HoRNDIS *)obj; 1188 | unsigned long poolIndx = (unsigned long)param; 1189 | 1190 | poolIndx = (unsigned long)param; 1191 | 1192 | LOG(V_PACKET, "(rc %08x, poolIndx %ld)", rc, poolIndx); 1193 | // Callback completed. We don't know when/if we launch another one: 1194 | me->callbackExit(); 1195 | 1196 | // Note, if 'fReadyToTransfer' is false, we shall not go further: 1197 | // it's a good idea NOT to touch the 'outbufs'. 1198 | if (isTransferStopStatus(rc) || !me->fReadyToTransfer) { 1199 | LOG(V_DEBUG, "Data Write Aborted, or ready-to-transfer is cleared."); 1200 | return; 1201 | } 1202 | 1203 | if (rc != kIOReturnSuccess) { 1204 | // Write error. In case of pipe stall, 1205 | // we shall clear it on the next transmit. 1206 | LOG(V_ERROR, "I/O error: %08x", rc); 1207 | } 1208 | 1209 | // Free the buffer: put the index back onto the stack: 1210 | if (me->numFreeOutBufs >= N_OUT_BUFS) { 1211 | LOG(V_ERROR, "BUG: more free buffers than was allocated"); 1212 | return; 1213 | } 1214 | 1215 | me->outbufStack[me->numFreeOutBufs] = poolIndx; 1216 | me->numFreeOutBufs++; 1217 | // Unstall the queue whenever the number of free buffers goes 0->1. 1218 | // I.e. we unstall it the moment we're able to write something into it: 1219 | if (me->numFreeOutBufs == 1) { 1220 | me->getOutputQueue()->service(); 1221 | } 1222 | } 1223 | 1224 | 1225 | /***** Packet receive logic *****/ 1226 | void HoRNDIS::dataReadComplete(void *obj, void *param, IOReturn rc, UInt32 transferred) { 1227 | HoRNDIS *me = (HoRNDIS *)obj; 1228 | pipebuf_t *inbuf = (pipebuf_t *)param; 1229 | IOReturn ior; 1230 | 1231 | // Stop conditions. Not separating them out, since reacting to individual 1232 | // ones would be very timing-sansitive. 1233 | if (isTransferStopStatus(rc) || !me->fReadyToTransfer) { 1234 | LOG(V_DEBUG, "READER STOPPED: USB device aborted or not responding, " 1235 | "or 'fReadyToTransfer' flag is cleared."); 1236 | me->callbackExit(); 1237 | return; 1238 | } 1239 | 1240 | if (rc == kIOReturnSuccess) { 1241 | // Got one? Hand it to the back end. 1242 | LOG(V_PACKET, "Reader(%ld), tid=%lld: %d bytes", inbuf - me->inbufs, 1243 | thread_tid(current_thread()), transferred); 1244 | me->receivePacket(inbuf->mdp->getBytesNoCopy(), transferred); 1245 | } else { 1246 | LOG(V_ERROR, "dataReadComplete: I/O error: %08x", rc); 1247 | } 1248 | 1249 | // Queue the next one up. 1250 | ior = robustIO(me->fInPipe, inbuf, (uint32_t)inbuf->mdp->getLength()); 1251 | if (ior == kIOReturnSuccess) { 1252 | return; // Callback is in-progress. 1253 | } 1254 | 1255 | LOG(V_ERROR, "READER STOPPED: USB failure trying to read: %08x", ior); 1256 | me->callbackExit(); 1257 | me->fDataDead = true; 1258 | } 1259 | 1260 | /*! 1261 | * Transfer the packet we've received to the MAC OS Network stack. 1262 | */ 1263 | void HoRNDIS::receivePacket(void *packet, UInt32 size) { 1264 | mbuf_t m; 1265 | UInt32 submit; 1266 | IOReturn rv; 1267 | 1268 | LOG(V_PACKET, "packet sz %d", (int)size); 1269 | 1270 | while (size) { 1271 | struct rndis_data_hdr *hdr = (struct rndis_data_hdr *)packet; 1272 | uint32_t msg_len, data_ofs, data_len; 1273 | 1274 | if (size <= sizeof(struct rndis_data_hdr)) { 1275 | LOG(V_ERROR, "receivePacket() on too small packet? (size %d)", size); 1276 | return; 1277 | } 1278 | 1279 | msg_len = le32_to_cpu(hdr->msg_len); 1280 | data_ofs = le32_to_cpu(hdr->data_offset); 1281 | data_len = le32_to_cpu(hdr->data_len); 1282 | 1283 | if (hdr->msg_type != RNDIS_MSG_PACKET) { // both are LE, so that's okay 1284 | LOG(V_ERROR, "non-PACKET over data channel? (msg_type %08x)", hdr->msg_type); 1285 | return; 1286 | } 1287 | 1288 | if (msg_len > size) { 1289 | LOG(V_ERROR, "msg_len too big?"); 1290 | return; 1291 | } 1292 | 1293 | if ((data_ofs + data_len + 8) > msg_len) { 1294 | LOG(V_ERROR, "data bigger than msg?"); 1295 | return; 1296 | } 1297 | 1298 | m = allocatePacket(data_len); 1299 | if (!m) { 1300 | LOG(V_ERROR, "allocatePacket for data_len %d failed", data_len); 1301 | fpNetStats->inputErrors++; 1302 | return; 1303 | } 1304 | LOG(V_PTR, "PTR: mbuf: %p", m); 1305 | 1306 | rv = mbuf_copyback(m, 0, data_len, (char *)packet + data_ofs + 8, MBUF_WAITOK); 1307 | if (rv) { 1308 | LOG(V_ERROR, "mbuf_copyback failed, rv %08x", rv); 1309 | fpNetStats->inputErrors++; 1310 | freePacket(m); 1311 | return; 1312 | } 1313 | 1314 | submit = fNetworkInterface->inputPacket(m, data_len); 1315 | LOG(V_PACKET, "submitted pkt sz %d", data_len); 1316 | fpNetStats->inputPackets++; 1317 | 1318 | size -= msg_len; 1319 | packet = (char *)packet + msg_len; 1320 | } 1321 | } 1322 | 1323 | 1324 | /***** RNDIS command logic *****/ 1325 | 1326 | IOReturn HoRNDIS::rndisCommand(struct rndis_msg_hdr *buf, int buflen) { 1327 | int rc = kIOReturnSuccess; 1328 | if (!fCommInterface) { // Safety: make sure 'fCommInterface' is valid. 1329 | LOG(V_ERROR, "fCommInterface is NULL, bailing out"); 1330 | return kIOReturnError; 1331 | } 1332 | const uint8_t ifNum = fCommInterface->getInterfaceDescriptor()->bInterfaceNumber; 1333 | 1334 | if (buf->msg_type != RNDIS_MSG_HALT && buf->msg_type != RNDIS_MSG_RESET) { 1335 | // No need to lock here: multi-threading does not even come close 1336 | // (IOWorkLoop + IOGate are at our service): 1337 | buf->request_id = cpu_to_le32(rndisXid++); 1338 | if (!buf->request_id) { 1339 | buf->request_id = cpu_to_le32(rndisXid++); 1340 | } 1341 | 1342 | LOG(V_DEBUG, "Generated xid: %d", le32_to_cpu(buf->request_id)); 1343 | } 1344 | const uint32_t old_msg_type = buf->msg_type; 1345 | const uint32_t old_request_id = buf->request_id; 1346 | 1347 | { 1348 | DeviceRequest rq; 1349 | rq.bmRequestType = kDeviceRequestDirectionOut | 1350 | kDeviceRequestTypeClass | kDeviceRequestRecipientInterface; 1351 | rq.bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 1352 | rq.wValue = 0; 1353 | rq.wIndex = ifNum; 1354 | rq.wLength = le32_to_cpu(buf->msg_len); 1355 | 1356 | uint32_t bytes_transferred; 1357 | if ((rc = fCommInterface->deviceRequest(rq, buf, bytes_transferred)) != kIOReturnSuccess) { 1358 | LOG(V_DEBUG, "Device request send error"); 1359 | return rc; 1360 | } 1361 | if (bytes_transferred != rq.wLength) { 1362 | LOG(V_DEBUG, "Incomplete device transfer"); 1363 | return kIOReturnError; 1364 | } 1365 | } 1366 | 1367 | // The RNDIS control messages are done via 'deviceRequest' - issue control 1368 | // transfers on the device's default endpoint. Per [MSDN-RNDISUSB], if 1369 | // a device is not ready (for some reason) to reply with the actual data, 1370 | // it shall send a one-byte reply indicating an error, rather than stall 1371 | // the control pipe. The retry loop below is a hackish way of waiting 1372 | // for the reply. 1373 | // 1374 | // Per [MSDN-RNDISUSB], once the driver sends a OUT device transfer, it 1375 | // should wait for a notification on the interrupt endpoint from 1376 | // fCommInterface, and only then perform a device request to retrieve 1377 | // the result. Whether Android does that correctly is something I need to 1378 | // investigate. 1379 | // 1380 | // Also, RNDIS specifies that the device may be sending 1381 | // REMOTE_NDIS_INDICATE_STATUS_MSG on its own. How much this applies to 1382 | // Android or embedded Linux devices needs to be investigated. 1383 | // 1384 | // Reference: 1385 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/network/control-channel-characteristics 1386 | 1387 | // Now we wait around a while for the device to get back to us. 1388 | int count; 1389 | for (count = 0; count < 10; count++) { 1390 | DeviceRequest rq; 1391 | rq.bmRequestType = kDeviceRequestDirectionIn | 1392 | kDeviceRequestTypeClass | kDeviceRequestRecipientInterface; 1393 | rq.bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 1394 | rq.wValue = 0; 1395 | rq.wIndex = ifNum; 1396 | rq.wLength = RNDIS_CMD_BUF_SZ; 1397 | 1398 | // Make sure 'fCommInterface' was not taken away from us while 1399 | // we were doing synchronous IO: 1400 | if (!fCommInterface) { 1401 | LOG(V_ERROR, "fCommInterface was closed, bailing out"); 1402 | return kIOReturnError; 1403 | } 1404 | uint32_t bytes_transferred; 1405 | if ((rc = fCommInterface->deviceRequest(rq, buf, bytes_transferred)) != kIOReturnSuccess) { 1406 | return rc; 1407 | } 1408 | 1409 | if (bytes_transferred < 12) { 1410 | LOG(V_ERROR, "short read on control request?"); 1411 | IOSleep(20); 1412 | continue; 1413 | } 1414 | 1415 | if (buf->msg_type == (old_msg_type | RNDIS_MSG_COMPLETION)) { 1416 | if (buf->request_id == old_request_id) { 1417 | if (buf->msg_type == RNDIS_MSG_RESET_C) { 1418 | // This is probably incorrect: the RESET_C does not have 1419 | // 'request_id', but we don't issue resets => don't care. 1420 | break; 1421 | } 1422 | if (buf->status != RNDIS_STATUS_SUCCESS) { 1423 | LOG(V_ERROR, "RNDIS command returned status %08x", 1424 | le32_to_cpu(buf->status)); 1425 | rc = kIOReturnError; 1426 | break; 1427 | } 1428 | if (le32_to_cpu(buf->msg_len) != bytes_transferred) { 1429 | LOG(V_ERROR, "Message Length mismatch: expected: %d, actual: %d", 1430 | le32_to_cpu(buf->msg_len), bytes_transferred); 1431 | rc = kIOReturnError; 1432 | break; 1433 | } 1434 | LOG(V_DEBUG, "RNDIS command completed"); 1435 | break; 1436 | } else { 1437 | LOG(V_ERROR, "RNDIS return had incorrect xid?"); 1438 | } 1439 | } else { 1440 | if (buf->msg_type == RNDIS_MSG_INDICATE) { 1441 | LOG(V_ERROR, "unsupported: RNDIS_MSG_INDICATE"); 1442 | } else if (buf->msg_type == RNDIS_MSG_INDICATE) { 1443 | LOG(V_ERROR, "unsupported: RNDIS_MSG_KEEPALIVE"); 1444 | } else { 1445 | LOG(V_ERROR, "unexpected msg type %08x, msg_len %08x", 1446 | le32_to_cpu(buf->msg_type), le32_to_cpu(buf->msg_len)); 1447 | } 1448 | } 1449 | 1450 | IOSleep(20); 1451 | } 1452 | if (count == 10) { 1453 | LOG(V_ERROR, "command timed out?"); 1454 | return kIOReturnTimeout; 1455 | } 1456 | 1457 | return rc; 1458 | } 1459 | 1460 | int HoRNDIS::rndisQuery(void *buf, uint32_t oid, uint32_t in_len, void **reply, int *reply_len) { 1461 | int rc; 1462 | 1463 | union { 1464 | void *buf; 1465 | struct rndis_msg_hdr *hdr; 1466 | struct rndis_query *get; 1467 | struct rndis_query_c *get_c; 1468 | } u; 1469 | uint32_t off, len; 1470 | 1471 | u.buf = buf; 1472 | 1473 | memset(u.get, 0, sizeof(*u.get) + in_len); 1474 | u.get->msg_type = RNDIS_MSG_QUERY; 1475 | u.get->msg_len = cpu_to_le32(sizeof(*u.get) + in_len); 1476 | u.get->oid = oid; 1477 | u.get->len = cpu_to_le32(in_len); 1478 | u.get->offset = cpu_to_le32(20); 1479 | 1480 | rc = rndisCommand(u.hdr, RNDIS_CMD_BUF_SZ); 1481 | if (rc != kIOReturnSuccess) { 1482 | LOG(V_ERROR, "RNDIS_MSG_QUERY failure? %08x", rc); 1483 | return rc; 1484 | } 1485 | 1486 | off = le32_to_cpu(u.get_c->offset); 1487 | len = le32_to_cpu(u.get_c->len); 1488 | LOG(V_DEBUG, "RNDIS query completed"); 1489 | 1490 | if ((8 + off + len) > RNDIS_CMD_BUF_SZ) { 1491 | goto fmterr; 1492 | } 1493 | if (*reply_len != -1 && len != *reply_len) { 1494 | goto fmterr; 1495 | } 1496 | 1497 | *reply = ((unsigned char *) &u.get_c->request_id) + off; 1498 | *reply_len = len; 1499 | 1500 | return 0; 1501 | 1502 | fmterr: 1503 | LOG(V_ERROR, "protocol error?"); 1504 | return -1; 1505 | } 1506 | 1507 | bool HoRNDIS::rndisInit() { 1508 | int rc; 1509 | union { 1510 | struct rndis_msg_hdr *hdr; 1511 | struct rndis_init *init; 1512 | struct rndis_init_c *init_c; 1513 | } u; 1514 | 1515 | u.hdr = (rndis_msg_hdr *)IOMallocAligned(RNDIS_CMD_BUF_SZ, sizeof(void *)); 1516 | if (!u.hdr) { 1517 | LOG(V_ERROR, "out of memory?"); 1518 | return false; 1519 | } 1520 | 1521 | u.init->msg_type = RNDIS_MSG_INIT; 1522 | u.init->msg_len = cpu_to_le32(sizeof *u.init); 1523 | u.init->major_version = cpu_to_le32(1); 1524 | u.init->minor_version = cpu_to_le32(0); 1525 | // This is the maximum USB transfer the device is allowed to make to host: 1526 | u.init->max_transfer_size = IN_BUF_SIZE; 1527 | rc = rndisCommand(u.hdr, RNDIS_CMD_BUF_SZ); 1528 | if (rc != kIOReturnSuccess) { 1529 | LOG(V_ERROR, "INIT not successful?"); 1530 | IOFreeAligned(u.hdr, RNDIS_CMD_BUF_SZ); 1531 | return false; 1532 | } 1533 | 1534 | if (fCommInterface) { // Safety: don't accesss 'fCommInterface if NULL. 1535 | LOG(V_NOTE, "'%s': ver=%d.%d, max_packets_per_transfer=%d, " 1536 | "max_transfer_size=%d, packet_alignment=2^%d", 1537 | fCommInterface->getDevice()->getName(), 1538 | le32_to_cpu(u.init_c->major_version), 1539 | le32_to_cpu(u.init_c->minor_version), 1540 | le32_to_cpu(u.init_c->max_packets_per_transfer), 1541 | le32_to_cpu(u.init_c->max_transfer_size), 1542 | le32_to_cpu(u.init_c->packet_alignment)); 1543 | } 1544 | 1545 | maxOutTransferSize = le32_to_cpu(u.init_c->max_transfer_size); 1546 | // For now, let's limit the maxOutTransferSize by the Output Buffer size. 1547 | // If we implement transmitting multiple PDUs in a single USB transfer, 1548 | // we may want to size the output buffers based on 1549 | // "u.init_c->max_transfer_size". 1550 | maxOutTransferSize = min(maxOutTransferSize, OUT_BUF_SIZE); 1551 | 1552 | IOFreeAligned(u.hdr, RNDIS_CMD_BUF_SZ); 1553 | 1554 | return true; 1555 | } 1556 | 1557 | bool HoRNDIS::rndisSetPacketFilter(uint32_t filter) { 1558 | union { 1559 | struct rndis_msg_hdr *hdr; 1560 | struct rndis_set *set; 1561 | struct rndis_set_c *set_c; 1562 | } u; 1563 | int rc; 1564 | 1565 | u.hdr = (rndis_msg_hdr *)IOMallocAligned(RNDIS_CMD_BUF_SZ, sizeof(void *)); 1566 | if (!u.hdr) { 1567 | LOG(V_ERROR, "out of memory?"); 1568 | return false;; 1569 | } 1570 | 1571 | memset(u.set, 0, sizeof *u.set); 1572 | u.set->msg_type = RNDIS_MSG_SET; 1573 | u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); 1574 | u.set->oid = OID_GEN_CURRENT_PACKET_FILTER; 1575 | u.set->len = cpu_to_le32(4); 1576 | u.set->offset = cpu_to_le32((sizeof *u.set) - 8); 1577 | *(uint32_t *)(u.set + 1) = filter; 1578 | 1579 | rc = rndisCommand(u.hdr, RNDIS_CMD_BUF_SZ); 1580 | if (rc != kIOReturnSuccess) { 1581 | LOG(V_ERROR, "SET not successful?"); 1582 | IOFreeAligned(u.hdr, RNDIS_CMD_BUF_SZ); 1583 | return false; 1584 | } 1585 | 1586 | IOFreeAligned(u.hdr, RNDIS_CMD_BUF_SZ); 1587 | 1588 | return true; 1589 | } 1590 | -------------------------------------------------------------------------------- /HoRNDIS.h: -------------------------------------------------------------------------------- 1 | /* HoRNDIS.h 2 | * Declaration of IOKit-derived classes 3 | * HoRNDIS, a RNDIS driver for Mac OS X 4 | * 5 | * Copyright (c) 2012 Joshua Wise. 6 | * Copyright (c) 2018 Mikhail Iakhiaev 7 | * 8 | * IOKit examples from Apple's USBCDCEthernet.cpp; not much of that code remains. 9 | * 10 | * RNDIS logic is from linux/drivers/net/usb/rndis_host.c, which is: 11 | * 12 | * Copyright (c) 2005 David Brownell. 13 | * 14 | * 15 | * This program is free software; you can redistribute it and/or modify 16 | * it under the terms of the GNU General Public License as published by 17 | * the Free Software Foundation; either version 2 of the License, or 18 | * (at your option) any later version. 19 | * 20 | * This program is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * You should have received a copy of the GNU General Public License 26 | * along with this program; if not, write to the Free Software 27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 | */ 29 | 30 | #include /* UINT_MAX */ 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | 47 | extern "C" 48 | { 49 | #include 50 | #include 51 | } 52 | 53 | #define cpu_to_le32(x) OSSwapHostToLittleInt32(x) 54 | #define le32_to_cpu(x) OSSwapLittleToHostInt32(x) 55 | // Helps to avoid including private classes and methods into the symbol table. 56 | #define NOEXPORT __attribute__((visibility("hidden"))) 57 | 58 | // REFERENCES: 59 | // [MS-RNDIS]: Remote Network Driver Interface Specification (RNDIS) Protocol 60 | // https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/WinArchive/[MS-RNDIS].pdf 61 | // [MSDN-RNDISUSB]: Remote NDIS To USB Mapping 62 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/network/remote-ndis-to-usb-mapping 63 | 64 | #define TRANSMIT_QUEUE_SIZE 256 65 | #define OUT_BUF_SIZE 4096 66 | 67 | // Per [MS-RNDIS], description of REMOTE_NDIS_INITIALIZE_MSG: 68 | // "MaxTransferSize (4 bytes): ... It SHOULD be set to 0x00004000" 69 | // I.e. specs recommends we should be able to input 16K in a single transfer. 70 | // Also, some Android versions (e.g. 8.1.0 on Pixel 2) seem to ignore 71 | // "max_transfer_size" in "REMOTE_NDIS_INITIALIZE_MSG" and use packets up to 72 | // 16K regardless. 73 | #define IN_BUF_SIZE 16384 74 | 75 | #define N_OUT_BUFS 4 76 | // The N_IN_BUFS value should either be 1 or 2. 77 | // 2 - double-buffering enabled, 1 - double-buffering disabled: single reader. 78 | // NOTE: surprisingly, single-buffer overall performs better, probably due to 79 | // less contention on the USB2 bus, which is half-duplex. 80 | #define N_IN_BUFS 1 81 | 82 | // Maximum payload size in a standard (non-jumbo) Ethernet frame. 83 | #define ETHERNET_MTU 1500 84 | 85 | /***** RNDIS definitions -- from linux/include/linux/usb/rndis_host.h ****/ 86 | 87 | // Per [MSDN-RNDISUSB], "Control Channel Characteristics", it's the minumim 88 | // buffer size the host should support (and it's way bigger than we need). 89 | #define RNDIS_CMD_BUF_SZ 0x400 90 | 91 | struct rndis_msg_hdr { 92 | uint32_t msg_type; 93 | uint32_t msg_len; 94 | uint32_t request_id; 95 | uint32_t status; 96 | } __attribute__((packed)); 97 | 98 | struct rndis_data_hdr { 99 | uint32_t msg_type; 100 | uint32_t msg_len; 101 | uint32_t data_offset; 102 | uint32_t data_len; 103 | 104 | uint32_t oob_data_offset; 105 | uint32_t oob_data_len; 106 | uint32_t num_oob; 107 | uint32_t packet_data_offset; 108 | 109 | uint32_t packet_data_len; 110 | uint32_t vc_handle; 111 | uint32_t reserved; 112 | } __attribute__((packed)); 113 | 114 | struct rndis_query { 115 | uint32_t msg_type; 116 | uint32_t msg_len; 117 | uint32_t request_id; 118 | uint32_t oid; 119 | uint32_t len; 120 | uint32_t offset; 121 | uint32_t handle; 122 | } __attribute__((packed)); 123 | 124 | struct rndis_query_c { 125 | uint32_t msg_type; 126 | uint32_t msg_len; 127 | uint32_t request_id; 128 | uint32_t status; 129 | uint32_t len; 130 | uint32_t offset; 131 | } __attribute__((packed)); 132 | 133 | struct rndis_init { 134 | uint32_t msg_type; 135 | uint32_t msg_len; 136 | uint32_t request_id; 137 | uint32_t major_version; 138 | uint32_t minor_version; 139 | uint32_t max_transfer_size; 140 | } __attribute__((packed)); 141 | 142 | struct rndis_init_c { 143 | uint32_t msg_type; 144 | uint32_t msg_len; 145 | uint32_t request_id; 146 | uint32_t status; 147 | uint32_t major_version; 148 | uint32_t minor_version; 149 | uint32_t device_flags; 150 | uint32_t medium; 151 | uint32_t max_packets_per_transfer; 152 | uint32_t max_transfer_size; 153 | uint32_t packet_alignment; 154 | uint32_t af_list_offset; 155 | uint32_t af_list_size; 156 | } __attribute__((packed)); 157 | 158 | struct rndis_set { 159 | uint32_t msg_type; 160 | uint32_t msg_len; 161 | uint32_t request_id; 162 | uint32_t oid; 163 | uint32_t len; 164 | uint32_t offset; 165 | uint32_t handle; 166 | } __attribute__((packed)); 167 | 168 | struct rndis_set_c { 169 | uint32_t msg_type; 170 | uint32_t msg_len; 171 | uint32_t request_id; 172 | uint32_t status; 173 | } __attribute__((packed)); 174 | 175 | #define RNDIS_MSG_COMPLETION cpu_to_le32(0x80000000) 176 | #define RNDIS_MSG_PACKET cpu_to_le32(0x00000001) /* 1-N packets */ 177 | #define RNDIS_MSG_INIT cpu_to_le32(0x00000002) 178 | #define RNDIS_MSG_INIT_C (RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION) 179 | #define RNDIS_MSG_HALT cpu_to_le32(0x00000003) 180 | #define RNDIS_MSG_QUERY cpu_to_le32(0x00000004) 181 | #define RNDIS_MSG_QUERY_C (RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION) 182 | #define RNDIS_MSG_SET cpu_to_le32(0x00000005) 183 | #define RNDIS_MSG_SET_C (RNDIS_MSG_SET|RNDIS_MSG_COMPLETION) 184 | #define RNDIS_MSG_RESET cpu_to_le32(0x00000006) 185 | #define RNDIS_MSG_RESET_C (RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION) 186 | #define RNDIS_MSG_INDICATE cpu_to_le32(0x00000007) 187 | #define RNDIS_MSG_KEEPALIVE cpu_to_le32(0x00000008) 188 | #define RNDIS_MSG_KEEPALIVE_C (RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION) 189 | 190 | #define RNDIS_STATUS_SUCCESS cpu_to_le32(0x00000000) 191 | #define RNDIS_STATUS_FAILURE cpu_to_le32(0xc0000001) 192 | #define RNDIS_STATUS_INVALID_DATA cpu_to_le32(0xc0010015) 193 | #define RNDIS_STATUS_NOT_SUPPORTED cpu_to_le32(0xc00000bb) 194 | #define RNDIS_STATUS_MEDIA_CONNECT cpu_to_le32(0x4001000b) 195 | #define RNDIS_STATUS_MEDIA_DISCONNECT cpu_to_le32(0x4001000c) 196 | #define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION cpu_to_le32(0x40010012) 197 | 198 | #define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED cpu_to_le32(0x00000000) 199 | #define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN cpu_to_le32(0x00000001) 200 | #define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM cpu_to_le32(0x00000002) 201 | #define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE cpu_to_le32(0x00000003) 202 | #define RNDIS_PHYSICAL_MEDIUM_POWER_LINE cpu_to_le32(0x00000004) 203 | #define RNDIS_PHYSICAL_MEDIUM_DSL cpu_to_le32(0x00000005) 204 | #define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL cpu_to_le32(0x00000006) 205 | #define RNDIS_PHYSICAL_MEDIUM_1394 cpu_to_le32(0x00000007) 206 | #define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN cpu_to_le32(0x00000008) 207 | #define RNDIS_PHYSICAL_MEDIUM_MAX cpu_to_le32(0x00000009) 208 | 209 | #define OID_802_3_PERMANENT_ADDRESS cpu_to_le32(0x01010101) 210 | #define OID_GEN_MAXIMUM_FRAME_SIZE cpu_to_le32(0x00010106) 211 | #define OID_GEN_CURRENT_PACKET_FILTER cpu_to_le32(0x0001010e) 212 | #define OID_GEN_PHYSICAL_MEDIUM cpu_to_le32(0x00010202) 213 | 214 | /* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */ 215 | #define RNDIS_PACKET_TYPE_DIRECTED cpu_to_le32(0x00000001) 216 | #define RNDIS_PACKET_TYPE_MULTICAST cpu_to_le32(0x00000002) 217 | #define RNDIS_PACKET_TYPE_ALL_MULTICAST cpu_to_le32(0x00000004) 218 | #define RNDIS_PACKET_TYPE_BROADCAST cpu_to_le32(0x00000008) 219 | #define RNDIS_PACKET_TYPE_SOURCE_ROUTING cpu_to_le32(0x00000010) 220 | #define RNDIS_PACKET_TYPE_PROMISCUOUS cpu_to_le32(0x00000020) 221 | #define RNDIS_PACKET_TYPE_SMT cpu_to_le32(0x00000040) 222 | #define RNDIS_PACKET_TYPE_ALL_LOCAL cpu_to_le32(0x00000080) 223 | #define RNDIS_PACKET_TYPE_GROUP cpu_to_le32(0x00001000) 224 | #define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL cpu_to_le32(0x00002000) 225 | #define RNDIS_PACKET_TYPE_FUNCTIONAL cpu_to_le32(0x00004000) 226 | #define RNDIS_PACKET_TYPE_MAC_FRAME cpu_to_le32(0x00008000) 227 | 228 | /* default filter used with RNDIS devices */ 229 | #define RNDIS_DEFAULT_FILTER ( \ 230 | RNDIS_PACKET_TYPE_DIRECTED | \ 231 | RNDIS_PACKET_TYPE_BROADCAST | \ 232 | RNDIS_PACKET_TYPE_ALL_MULTICAST | \ 233 | RNDIS_PACKET_TYPE_PROMISCUOUS) 234 | 235 | #define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 236 | #define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 237 | 238 | /***** Actual class definitions *****/ 239 | 240 | typedef struct { 241 | IOBufferMemoryDescriptor *mdp; 242 | IOUSBHostCompletion comp; 243 | } pipebuf_t; 244 | 245 | class HoRNDIS : public IOEthernetController { 246 | OSDeclareDefaultStructors(HoRNDIS); // Constructor & Destructor stuff 247 | 248 | private: 249 | /*! 250 | * This class protects method calls against re-entry, when the IOCommand 251 | * gate is being released due to synchronous IO, or some other reason. 252 | * Use case: 253 | * Unlike start/stop calls, that are triggered by a single IO provider, 254 | * the enable/disable calls can be triggered by multiple interface clients, 255 | * as well as user's actions, e.g. "ifconfig en6 up" - potentially multiple 256 | * "ifconfig" processes running in parallel. 257 | * Even though the calls to this class are protected by the IOCommandGate, 258 | * synchronous USB transfers release the gate (by using 259 | * IOCommandGate::commandSleep), allowing another enable/disable call 260 | * to "sneak in". We use the "ReentryLocker" to delay additional 261 | * enable/disable calls until the first one completes. 262 | */ 263 | class NOEXPORT ReentryLocker { 264 | public: 265 | // 'inGuard' is instance-level variable that would be set by 266 | // "ReentryLocker" whenever someone is executing the protected section. 267 | ReentryLocker(IOCommandGate *inGate, bool &inGuard); 268 | ReentryLocker(IONetworkController *controller, bool &inGuard): 269 | ReentryLocker(controller->getCommandGate(), inGuard) {} 270 | ~ReentryLocker(); 271 | IOReturn getResult() const { return result; } 272 | bool isInterrupted() const { return result != kIOReturnSuccess; } 273 | private: 274 | IOCommandGate *const gate; 275 | bool &entryGuard; 276 | IOReturn result; 277 | }; 278 | 279 | IOEthernetInterface *fNetworkInterface; 280 | IONetworkStats *fpNetStats; 281 | 282 | bool fReadyToTransfer; // Ready to transmit: Android <-> MAC. 283 | // Set to true when 'enable' succeeds, and 284 | // set to false when 'disable' succeeds: 285 | bool fNetifEnabled; 286 | bool fEnableDisableInProgress; // Guards against re-entry 287 | bool fDataDead; 288 | 289 | // These pass information from 'probe' to 'openUSBInterfaces': 290 | uint8_t fProbeConfigVal; 291 | uint8_t fProbeCommIfNum; // The data interface number is +1. 292 | 293 | // fCallbackCount is the number of callbacks concurrently running 294 | // (possibly offset by a certain value). 295 | // - Every successful async API call shall "fCallbackCount++". 296 | // - Every time we exit the completion without making another call: 297 | // 'callbackExit()'. 298 | int fCallbackCount; 299 | 300 | // USB Communication: 301 | IOUSBHostInterface *fCommInterface; 302 | IOUSBHostInterface *fDataInterface; 303 | 304 | IOUSBHostPipe *fInPipe; 305 | IOUSBHostPipe *fOutPipe; 306 | 307 | uint32_t rndisXid; // RNDIS request_id count. 308 | int32_t maxOutTransferSize; // Set by 'rdisInit' from device reply. 309 | 310 | pipebuf_t outbufs[N_OUT_BUFS]; 311 | // Allow double-buffering to enable the best hardware utilization: 312 | pipebuf_t inbufs[N_IN_BUFS]; 313 | uint16_t outbufStack[N_OUT_BUFS]; 314 | int numFreeOutBufs; 315 | 316 | void callbackExit(); 317 | static void dataWriteComplete(void *obj, void *param, IOReturn ior, UInt32 transferred); 318 | static void dataReadComplete(void *obj, void *param, IOReturn ior, UInt32 transferred); 319 | 320 | bool rndisInit(); 321 | IOReturn rndisCommand(struct rndis_msg_hdr *buf, int buflen); 322 | int rndisQuery(void *buf, uint32_t oid, uint32_t in_len, void **reply, int *reply_len); 323 | bool rndisSetPacketFilter(uint32_t filter); 324 | 325 | IOService *probeDevice(IOUSBHostDevice *device, SInt32 *score); 326 | 327 | bool openUSBInterfaces(IOService *provider); 328 | void closeUSBInterfaces(); 329 | void disableNetworkQueue(); 330 | void disableImpl(); 331 | 332 | bool createMediumTables(const IONetworkMedium **primary); 333 | bool allocateResources(void); 334 | void releaseResources(void); 335 | bool createNetworkInterface(void); 336 | 337 | void receivePacket(void *packet, UInt32 size); 338 | 339 | public: 340 | // IOKit overrides 341 | virtual bool init(OSDictionary *properties = 0) override; 342 | virtual void free() override; 343 | virtual IOService *probe(IOService *provider, SInt32 *score) override; 344 | virtual bool start(IOService *provider) override; 345 | virtual bool willTerminate(IOService *provider, IOOptionBits options) override; 346 | virtual void stop(IOService *provider) override; 347 | 348 | // IOEthernetController overrides 349 | virtual IOOutputQueue *createOutputQueue(void) override; 350 | virtual IOReturn getHardwareAddress(IOEthernetAddress *addr) override; 351 | virtual IOReturn getMaxPacketSize(UInt32 *maxSize) const override; 352 | virtual IOReturn getPacketFilters(const OSSymbol *group, 353 | UInt32 *filters ) const override; 354 | virtual IONetworkInterface *createInterface() override; 355 | virtual bool configureInterface(IONetworkInterface *netif) override; 356 | 357 | virtual IOReturn enable(IONetworkInterface *netif) override; 358 | virtual IOReturn disable(IONetworkInterface *netif) override; 359 | virtual IOReturn selectMedium(const IONetworkMedium *medium) override; 360 | virtual IOReturn setMulticastMode(bool active) override; 361 | virtual IOReturn setMulticastList(IOEthernetAddress *addrs, 362 | UInt32 count) override; 363 | virtual IOReturn setPromiscuousMode(bool active) override; 364 | virtual UInt32 outputPacket(mbuf_t pkt, void *param) override; 365 | }; 366 | 367 | class HoRNDISInterface : public IOEthernetInterface { 368 | OSDeclareDefaultStructors(HoRNDISInterface); 369 | int maxmtu; 370 | public: 371 | virtual bool init(IONetworkController *controller, int mtu); 372 | virtual bool setMaxTransferUnit(UInt32 mtu) override; 373 | }; 374 | -------------------------------------------------------------------------------- /HoRNDIS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 42BCD8B01645AFC500683BF7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 42BCD8AE1645AFC500683BF7 /* InfoPlist.strings */; }; 11 | 42BCD8B31645AFC500683BF7 /* HoRNDIS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42BCD8B21645AFC500683BF7 /* HoRNDIS.cpp */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | 42BCD8A61645AFC500683BF7 /* HoRNDIS.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HoRNDIS.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | 42BCD8AA1645AFC500683BF7 /* Kernel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kernel.framework; path = System/Library/Frameworks/Kernel.framework; sourceTree = SDKROOT; }; 17 | 42BCD8AD1645AFC500683BF7 /* HoRNDIS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HoRNDIS-Info.plist"; sourceTree = SOURCE_ROOT; }; 18 | 42BCD8AF1645AFC500683BF7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 19 | 42BCD8B11645AFC500683BF7 /* HoRNDIS.h */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = HoRNDIS.h; sourceTree = SOURCE_ROOT; tabWidth = 4; }; 20 | 42BCD8B21645AFC500683BF7 /* HoRNDIS.cpp */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HoRNDIS.cpp; sourceTree = SOURCE_ROOT; tabWidth = 4; }; 21 | 42BCD8B41645AFC500683BF7 /* HoRNDIS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HoRNDIS-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 22 | /* End PBXFileReference section */ 23 | 24 | /* Begin PBXFrameworksBuildPhase section */ 25 | 42BCD8A11645AFC500683BF7 /* Frameworks */ = { 26 | isa = PBXFrameworksBuildPhase; 27 | buildActionMask = 2147483647; 28 | files = ( 29 | ); 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXFrameworksBuildPhase section */ 33 | 34 | /* Begin PBXGroup section */ 35 | 42BCD8991645AFC500683BF7 = { 36 | isa = PBXGroup; 37 | children = ( 38 | 42BCD8AB1645AFC500683BF7 /* HoRNDIS */, 39 | 42BCD8A81645AFC500683BF7 /* Frameworks */, 40 | 42BCD8A71645AFC500683BF7 /* Products */, 41 | ); 42 | sourceTree = ""; 43 | usesTabs = 1; 44 | }; 45 | 42BCD8A71645AFC500683BF7 /* Products */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 42BCD8A61645AFC500683BF7 /* HoRNDIS.kext */, 49 | ); 50 | name = Products; 51 | sourceTree = ""; 52 | }; 53 | 42BCD8A81645AFC500683BF7 /* Frameworks */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 42BCD8A91645AFC500683BF7 /* Other Frameworks */, 57 | ); 58 | name = Frameworks; 59 | sourceTree = ""; 60 | }; 61 | 42BCD8A91645AFC500683BF7 /* Other Frameworks */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 42BCD8AA1645AFC500683BF7 /* Kernel.framework */, 65 | ); 66 | name = "Other Frameworks"; 67 | sourceTree = ""; 68 | }; 69 | 42BCD8AB1645AFC500683BF7 /* HoRNDIS */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 42BCD8B11645AFC500683BF7 /* HoRNDIS.h */, 73 | 42BCD8B21645AFC500683BF7 /* HoRNDIS.cpp */, 74 | 42BCD8AC1645AFC500683BF7 /* Supporting Files */, 75 | ); 76 | path = HoRNDIS; 77 | sourceTree = ""; 78 | }; 79 | 42BCD8AC1645AFC500683BF7 /* Supporting Files */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 42BCD8AD1645AFC500683BF7 /* HoRNDIS-Info.plist */, 83 | 42BCD8AE1645AFC500683BF7 /* InfoPlist.strings */, 84 | 42BCD8B41645AFC500683BF7 /* HoRNDIS-Prefix.pch */, 85 | ); 86 | name = "Supporting Files"; 87 | sourceTree = ""; 88 | }; 89 | /* End PBXGroup section */ 90 | 91 | /* Begin PBXHeadersBuildPhase section */ 92 | 42BCD8A21645AFC500683BF7 /* Headers */ = { 93 | isa = PBXHeadersBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXHeadersBuildPhase section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | 42BCD8A51645AFC500683BF7 /* HoRNDIS */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = 42BCD8B71645AFC500683BF7 /* Build configuration list for PBXNativeTarget "HoRNDIS" */; 105 | buildPhases = ( 106 | 42BCD8A01645AFC500683BF7 /* Sources */, 107 | 42BCD8A11645AFC500683BF7 /* Frameworks */, 108 | 42BCD8A21645AFC500683BF7 /* Headers */, 109 | 42BCD8A31645AFC500683BF7 /* Resources */, 110 | 42BCD8A41645AFC500683BF7 /* Rez */, 111 | ); 112 | buildRules = ( 113 | ); 114 | dependencies = ( 115 | ); 116 | name = HoRNDIS; 117 | productName = HoRNDIS; 118 | productReference = 42BCD8A61645AFC500683BF7 /* HoRNDIS.kext */; 119 | productType = "com.apple.product-type.kernel-extension"; 120 | }; 121 | /* End PBXNativeTarget section */ 122 | 123 | /* Begin PBXProject section */ 124 | 42BCD89B1645AFC500683BF7 /* Project object */ = { 125 | isa = PBXProject; 126 | attributes = { 127 | LastUpgradeCheck = 0920; 128 | ORGANIZATIONNAME = "Joshua Wise"; 129 | }; 130 | buildConfigurationList = 42BCD89E1645AFC500683BF7 /* Build configuration list for PBXProject "HoRNDIS" */; 131 | compatibilityVersion = "Xcode 6.3"; 132 | developmentRegion = English; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | ); 137 | mainGroup = 42BCD8991645AFC500683BF7; 138 | productRefGroup = 42BCD8A71645AFC500683BF7 /* Products */; 139 | projectDirPath = ""; 140 | projectRoot = ""; 141 | targets = ( 142 | 42BCD8A51645AFC500683BF7 /* HoRNDIS */, 143 | ); 144 | }; 145 | /* End PBXProject section */ 146 | 147 | /* Begin PBXResourcesBuildPhase section */ 148 | 42BCD8A31645AFC500683BF7 /* Resources */ = { 149 | isa = PBXResourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | 42BCD8B01645AFC500683BF7 /* InfoPlist.strings in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXRezBuildPhase section */ 159 | 42BCD8A41645AFC500683BF7 /* Rez */ = { 160 | isa = PBXRezBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXRezBuildPhase section */ 167 | 168 | /* Begin PBXSourcesBuildPhase section */ 169 | 42BCD8A01645AFC500683BF7 /* Sources */ = { 170 | isa = PBXSourcesBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | 42BCD8B31645AFC500683BF7 /* HoRNDIS.cpp in Sources */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXSourcesBuildPhase section */ 178 | 179 | /* Begin PBXVariantGroup section */ 180 | 42BCD8AE1645AFC500683BF7 /* InfoPlist.strings */ = { 181 | isa = PBXVariantGroup; 182 | children = ( 183 | 42BCD8AF1645AFC500683BF7 /* en */, 184 | ); 185 | name = InfoPlist.strings; 186 | sourceTree = SOURCE_ROOT; 187 | }; 188 | /* End PBXVariantGroup section */ 189 | 190 | /* Begin XCBuildConfiguration section */ 191 | 42BCD8B51645AFC500683BF7 /* Debug */ = { 192 | isa = XCBuildConfiguration; 193 | buildSettings = { 194 | ALWAYS_SEARCH_USER_PATHS = NO; 195 | CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; 196 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 197 | CLANG_WARN_BOOL_CONVERSION = YES; 198 | CLANG_WARN_COMMA = YES; 199 | CLANG_WARN_CONSTANT_CONVERSION = YES; 200 | CLANG_WARN_EMPTY_BODY = YES; 201 | CLANG_WARN_ENUM_CONVERSION = YES; 202 | CLANG_WARN_INFINITE_RECURSION = YES; 203 | CLANG_WARN_INT_CONVERSION = YES; 204 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 205 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 206 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 207 | CLANG_WARN_STRICT_PROTOTYPES = YES; 208 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 209 | CLANG_WARN_UNREACHABLE_CODE = YES; 210 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 211 | COPY_PHASE_STRIP = NO; 212 | DEAD_CODE_STRIPPING = YES; 213 | DEBUG_INFORMATION_FORMAT = dwarf; 214 | ENABLE_STRICT_OBJC_MSGSEND = YES; 215 | ENABLE_TESTABILITY = YES; 216 | GCC_C_LANGUAGE_STANDARD = c99; 217 | GCC_DYNAMIC_NO_PIC = NO; 218 | GCC_ENABLE_KERNEL_DEVELOPMENT = YES; 219 | GCC_INLINES_ARE_PRIVATE_EXTERN = YES; 220 | GCC_NO_COMMON_BLOCKS = YES; 221 | GCC_OPTIMIZATION_LEVEL = 0; 222 | GCC_PREPROCESSOR_DEFINITIONS = ( 223 | "DEBUG=1", 224 | "$(inherited)", 225 | ); 226 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 227 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 228 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 231 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 232 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 233 | GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; 234 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 235 | GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; 236 | GCC_WARN_SHADOW = YES; 237 | GCC_WARN_UNDECLARED_SELECTOR = YES; 238 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 239 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 240 | GCC_WARN_UNUSED_FUNCTION = YES; 241 | GCC_WARN_UNUSED_LABEL = YES; 242 | GCC_WARN_UNUSED_VARIABLE = YES; 243 | LLVM_LTO = YES; 244 | MACOSX_DEPLOYMENT_TARGET = 10.11; 245 | ONLY_ACTIVE_ARCH = YES; 246 | "OTHER_CPLUSPLUSFLAGS[arch=*]" = "-Wno-inconsistent-missing-override"; 247 | PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES; 248 | SDKROOT = macosx10.11; 249 | SYMROOT = "${SRCROOT}/build"; 250 | }; 251 | name = Debug; 252 | }; 253 | 42BCD8B61645AFC500683BF7 /* Release */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ALWAYS_SEARCH_USER_PATHS = NO; 257 | CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_INFINITE_RECURSION = YES; 265 | CLANG_WARN_INT_CONVERSION = YES; 266 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 269 | CLANG_WARN_STRICT_PROTOTYPES = YES; 270 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 271 | CLANG_WARN_UNREACHABLE_CODE = YES; 272 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 273 | COPY_PHASE_STRIP = YES; 274 | DEAD_CODE_STRIPPING = YES; 275 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | GCC_C_LANGUAGE_STANDARD = c99; 278 | GCC_ENABLE_KERNEL_DEVELOPMENT = YES; 279 | GCC_INLINES_ARE_PRIVATE_EXTERN = YES; 280 | GCC_NO_COMMON_BLOCKS = YES; 281 | GCC_OPTIMIZATION_LEVEL = fast; 282 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 283 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 286 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 287 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 288 | GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; 289 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 290 | GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; 291 | GCC_WARN_SHADOW = YES; 292 | GCC_WARN_UNDECLARED_SELECTOR = YES; 293 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 294 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 295 | GCC_WARN_UNUSED_FUNCTION = YES; 296 | GCC_WARN_UNUSED_LABEL = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | LLVM_LTO = YES; 299 | MACOSX_DEPLOYMENT_TARGET = 10.11; 300 | "OTHER_CPLUSPLUSFLAGS[arch=*]" = "-Wno-inconsistent-missing-override"; 301 | PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES; 302 | SDKROOT = macosx10.11; 303 | SYMROOT = "${SRCROOT}/build"; 304 | }; 305 | name = Release; 306 | }; 307 | 42BCD8B81645AFC500683BF7 /* Debug */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | CODE_SIGN_IDENTITY = "$(CODE_SIGN_$(USER))"; 311 | CODE_SIGN_iakhiaev = "Mac Developer: mikhailai@gmail.com (74ZR97MLB5)"; 312 | CODE_SIGN_joshua = "Developer ID Application: Joshua Wise (54GTJ2AU36)"; 313 | COMBINE_HIDPI_IMAGES = YES; 314 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 315 | GCC_PREFIX_HEADER = "HoRNDIS-Prefix.pch"; 316 | GCC_VERSION = ""; 317 | INFOPLIST_FILE = "HoRNDIS-Info.plist"; 318 | MODULE_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)"; 319 | MODULE_VERSION = 9.2; 320 | PRODUCT_BUNDLE_IDENTIFIER = com.joshuawise.kexts.HoRNDIS; 321 | PRODUCT_NAME = "$(TARGET_NAME)"; 322 | WRAPPER_EXTENSION = kext; 323 | }; 324 | name = Debug; 325 | }; 326 | 42BCD8B91645AFC500683BF7 /* Release */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | CODE_SIGN_IDENTITY = "$(CODE_SIGN_$(USER))"; 330 | CODE_SIGN_iakhiaev = "Mac Developer: mikhailai@gmail.com (74ZR97MLB5)"; 331 | CODE_SIGN_joshua = "Developer ID Application: Joshua Wise (54GTJ2AU36)"; 332 | COMBINE_HIDPI_IMAGES = YES; 333 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 334 | GCC_PREFIX_HEADER = "HoRNDIS-Prefix.pch"; 335 | GCC_VERSION = ""; 336 | INFOPLIST_FILE = "HoRNDIS-Info.plist"; 337 | MODULE_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)"; 338 | MODULE_VERSION = 9.2; 339 | PRODUCT_BUNDLE_IDENTIFIER = com.joshuawise.kexts.HoRNDIS; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | WRAPPER_EXTENSION = kext; 342 | }; 343 | name = Release; 344 | }; 345 | /* End XCBuildConfiguration section */ 346 | 347 | /* Begin XCConfigurationList section */ 348 | 42BCD89E1645AFC500683BF7 /* Build configuration list for PBXProject "HoRNDIS" */ = { 349 | isa = XCConfigurationList; 350 | buildConfigurations = ( 351 | 42BCD8B51645AFC500683BF7 /* Debug */, 352 | 42BCD8B61645AFC500683BF7 /* Release */, 353 | ); 354 | defaultConfigurationIsVisible = 0; 355 | defaultConfigurationName = Release; 356 | }; 357 | 42BCD8B71645AFC500683BF7 /* Build configuration list for PBXNativeTarget "HoRNDIS" */ = { 358 | isa = XCConfigurationList; 359 | buildConfigurations = ( 360 | 42BCD8B81645AFC500683BF7 /* Debug */, 361 | 42BCD8B91645AFC500683BF7 /* Release */, 362 | ); 363 | defaultConfigurationIsVisible = 0; 364 | defaultConfigurationName = Release; 365 | }; 366 | /* End XCConfigurationList section */ 367 | }; 368 | rootObject = 42BCD89B1645AFC500683BF7 /* Project object */; 369 | } 370 | -------------------------------------------------------------------------------- /KNOWN_BUGS: -------------------------------------------------------------------------------- 1 | The following bugs have been sighted, but not root-caused: 2 | 3 | Certain sequences of loading and unloading the driver (and connecting and 4 | disconnecting the phone) can cause the driver to leak a refcount to itself. 5 | For instance, the sequence -- 6 | plug in phone 7 | set phone to tethering 8 | load driver 9 | do some operations... 10 | unload driver 11 | load driver 12 | (checkbox on phone will have unticked itself; phone will be confused) 13 | driver wedges during ::enable, since rndis driver on phone has died 14 | unplug phone 15 | attempt to unload driver 16 | results in the message -- 17 | (kernel) Can't unload kext com.joshuawise.kexts.HoRNDIS; classes have instances: 18 | (kernel) Kext com.joshuawise.kexts.HoRNDIS class HoRNDIS has 1 instance. 19 | and the interesting commentary from the kernel -- 20 | bruges kernel[0]: HoRNDIS: outputPacket: waiting for buffer... 21 | --- last message repeated 9 times --- 22 | bruges kernel[0]: HoRNDIS: outputPacket: timed out waiting for buffer 23 | bruges kernel[0]: HoRNDIS: dataWriteComplete: I/O error: e00002ed 24 | bruges kernel[0]: HoRNDIS: clearPipeStall: pipe not stalled? 25 | bruges kernel[0]: HoRNDIS: dataReadComplete: I/O aborted: device unplugged? 26 | bruges.joshuawise.com configd[17]: DHCP en3: INIT transmit failed 27 | bruges.joshuawise.com configd[17]: arp_client_open_fd: bpf_setif(en3) failed: Device not configured (6) 28 | bruges kernel[0]: HoRNDIS: message: kIOMessageServiceWasClosed 29 | bruges kernel[0]: HoRNDIS: rndisSetPacketFilter: SET not successful? 30 | bruges kernel[0]: HoRNDIS: message: kIOMessageServiceIsTerminated 31 | bruges kernel[0]: HoRNDIS: dataWriteComplete: I/O error: e00002eb 32 | --- last message repeated 14 times --- 33 | 34 | 35 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # For the building the driver for MacOS 10.11+, the preferred 2 | # XCode version is 7.3.1. 3 | # 4 | # You can do this by downloading Xcode 7.3.1 as a dmg from Apple, then 5 | # copying the contents into /Applications as Xcode-7.3.1. 6 | # If you also want to build from "release_pre_10_11", you may want to 7 | # also download "Xcode-4.3.3", and copy "MacOSX10.6.sdk" from it to 8 | # SDK directory under: 9 | # /Applications/Xcode-7.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ 10 | 11 | -include localconfig.mk 12 | 13 | # Can be set from the environment: 14 | HORNDIS_XCODE ?= /Applications/Xcode*$(XCODE_VER).app 15 | 16 | XCODE_VER ?= 7.3.1 17 | XCODEBUILD ?= $(wildcard $(HORNDIS_XCODE)/Contents/Developer/usr/bin/xcodebuild) 18 | 19 | ifeq (,$(XCODEBUILD)) 20 | $(error Cannot find xcodebuild under $(HORNDIS_XCODE). Please either \ 21 | download Xcode $(XCODE_VER) from: "https://developer.apple.com/download" \ 22 | and install as /Applications/Xcode-$(XCODE_VER)/ or point HORNDIS_XCODE \ 23 | to your preferred Xcode app path) 24 | endif 25 | 26 | # The package signing certificate must either be set or explicitly disabled: 27 | ifeq (,$(CODESIGN_INST)) 28 | $(error Please set CODESIGN_INST variable to your Mac Installer \ 29 | certificate or 'none' if you don't have any. \ 30 | E.g. "export CODESIGN_INST=G3H8VBSL7A") 31 | else ifeq (none,$(CODESIGN_INST)) 32 | # Clear the 'none' vaulue: easier to test in 'if' condition. 33 | CODESIGN_INST := 34 | endif 35 | 36 | all: build/Release/HoRNDIS.kext build/pkg/_complete 37 | 38 | clean: 39 | rm -rf build 40 | 41 | # We now sign as part of the xcodebuild process. 42 | build/Release/HoRNDIS.kext: $(wildcard *.cpp *.h *.plist HoRNDIS.xcodeproj/* *.lproj/*) 43 | $(XCODEBUILD) -project HoRNDIS.xcodeproj 44 | 45 | build/pkg/root: build/Release/HoRNDIS.kext 46 | rm -rf build/pkg/ 47 | mkdir -p build/pkg/root/Library/Extensions 48 | cp -R build/Release/HoRNDIS.kext build/pkg/root/Library/Extensions/ 49 | 50 | build/pkg/HoRNDIS-kext.pkg: build/pkg/root 51 | pkgbuild --identifier com.joshuawise.kexts.HoRNDIS --scripts package/scripts --root $< $@ 52 | 53 | # The variable is to be resolved first time it's used: 54 | VERSION = $(shell defaults read $(PWD)/build/Release/HoRNDIS.kext/Contents/Info.plist CFBundleVersion) 55 | 56 | build/pkg/_complete: build/pkg/HoRNDIS-kext.pkg $(wildcard package/*) 57 | productbuild --distribution package/Distribution.xml --package-path build/pkg --resources package --version $(VERSION) $(if $(CODESIGN_INST),--sign $(CODESIGN_INST)) build/HoRNDIS-$(VERSION).pkg && touch build/pkg/_complete 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HoRNDIS(the USB tethering driver for Mac OS X) 2 | 3 | **HoRNDIS** (pronounce: *"horrendous"*) is a driver for Mac OS X that allows you to use your Android phone's native [USB tethering](http://en.wikipedia.org/wiki/Tethering) mode to get Internet access. 4 | 5 | For more information, [visit the home page for HoRNDIS on my site](http://www.joshuawise.com/horndis). 6 | 7 | ## Installation 8 | 9 | ### From Source/Binary 10 | 11 | * Get the installation package ([Download](http://www.joshuawise.com/horndis) or [Build](#building-the-source) the installation package from source yourself) 12 | * Run the installation package 13 | 14 | ### From Homebrew 15 | 16 | ```sh 17 | brew cask install horndis 18 | sudo kextload /Library/Extensions/HoRNDIS.kext 19 | ``` 20 | 21 | ## Configuration 22 | 23 | * Assuming that the installation proceeds without errors, after it completes, connect your phone to your Mac by USB. 24 | * Enter the settings menu on your phone. 25 | * In the connections section, below Wi-Fi and Bluetooth: 26 | * Select "More..." 27 | * Select "Tethering & portable hotspot" 28 | * Check the "USB tethering" box. It should flash once, and then become solidly checked. 29 | 30 | ## Uninstallation 31 | 32 | * Delete the `HoRNDIS.kext` under `/System/Library/Extensions` and `/Library/Extensions` folder 33 | * Restart your computer 34 | 35 | ## Building the source 36 | 37 | * `git clone` the repository 38 | * Simply running xcodebuild in the checkout directory should be sufficient to build the kext. 39 | * If you wish to package it up, you can run `make` to assemble the package in the build/ directory 40 | 41 | ## Debugging and Development Notes 42 | 43 | This sections contains tips and tricks for developing and debugging the driver. 44 | 45 | ### USB Device Information 46 | 47 | *Mac OS System Menu* -> *About This Mac* -> *System Report* --> *Hardware*/*USB*
48 | Lists all USB devices that OS recognizes. Unfortunately, it does not give USB descriptors. 49 | 50 | `lsusb -v`
51 | It prints USB configuration, such as interface and endpoint descriptors. You can print it for all devices or limit the output to specific ones. In order to run this command, you need to install *usbutils*. 52 | * Homebrew users: `brew install mikhailai/misc/usbutils`
53 | Please *do not* install *lsusb* package from Homebrew Core, it's a different utility with the same name. 54 | * Macports users: `sudo port install usbutils` 55 | 56 | ### IO Registry 57 | 58 | `ioreg -l -r -c IOUSBHostDevice`
59 | This command lists all the Mac OS IO Registry information under all USB devices. Unlike *lsusb*, ioreg tells how Mac OS recognized USB devices and interfaces, and how it matched drivers to these interfaces. The `-r -c IOUSBHostDevice` limits the output to USB devices; to get complete OS registry, please run `ioreg -l`. 60 | 61 | ### OS Logging 62 | 63 | The `LOG(....)` statements, sprinkled throughout the HoRNDIS code, call the `IOLog` functions. On Mac OS *El Capitan* (10.11) and earlier, the log messages go into `/var/log/system.log`. Starting from *Sierra* (10.12), these messages are no longer written to `system.log` and instead can be viewed via: 64 | * *GUI*, using *Console* application, located in *Utilities* folder. You need to enter `process:kernel` in the search box in order to filter the relevant messages. 65 | * *Command Line*, using the `log` command. For example:
66 | `log show --predicate process==\"kernel\" --start "2018-12-11 22:54:00"`
67 | The start value needs to be several minutes in the past, so it would not flood the console. Here is a convenient command that prints the messages from the past 3 mintes:
68 | `log show --predicate process==\"kernel\" --start "$(date -v-3M +'%F %T')"` 69 | 70 | I've observed that Mac OS logging is unreliable (especially in *Sierra*). In some cases, the messages may come out garbled (looking like bad multi-threaded code). In other cases, either GUI or Command Line may be missing messages that were emitted. Sometimes, reloading the driver may fix the problem. 71 | -------------------------------------------------------------------------------- /en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /package/Distribution.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | HoRNDIS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | #HoRNDIS-kext.pkg 28 | 29 | -------------------------------------------------------------------------------- /package/intro-text.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830 2 | {\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;\f1\fnil\fcharset0 LucidaGrande;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}} 6 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} 7 | \margl1440\margr1440\vieww17840\viewh8040\viewkind0 8 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 9 | 10 | \f0\b\fs26 \cf0 HoRNDIS 11 | \f1\b0 is a kernel extension ("kext") that makes it possible to use your Android phone's built-in USB tethering support on your Mac OS X system. In order to use it, you will need:\ 12 | \ 13 | \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural\partightenfactor0 14 | \ls1\ilvl0\cf0 {\listtext \'95 }a Mac running OS X 10.11 or later (i.e., El Capitan, Sierra, High Sierra, or Mojave);\ 15 | {\listtext \'95 }an Android phone that supports USB tethering natively;\ 16 | {\listtext \'95 }and for the USB tethering feature on your phone to be enabled (either by your carrier, or through third-party firmware).\ 17 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 18 | \cf0 \ 19 | For support, please visit {\field{\*\fldinst{HYPERLINK "http://www.joshuawise.com/horndis"}}{\fldrslt http://www.joshuawise.com/horndis}}.\ 20 | \ 21 | You will be guided through the steps necessary to install 22 | \f0\b HoRNDIS 23 | \f1\b0 on your Mac.} -------------------------------------------------------------------------------- /package/post-readme.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 2 | {\fonttbl\f0\fnil\fcharset0 LucidaGrande;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}} 5 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} 6 | \margl1440\margr1440\vieww10800\viewh8400\viewkind0 7 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 8 | 9 | \f0\b\fs26 \cf0 HoRNDIS 10 | \b0 installation is complete. Depending on your version of OS X, you may need to restart your Mac for the driver to be loaded.\ 11 | \ 12 | To get started using 13 | \b HoRNDIS 14 | \b0 , you will need to enable tethering on your phone. To do so:\ 15 | \pard\tx220\tx687\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li658\fi-659\pardirnatural 16 | \ls1\ilvl0\cf0 {\listtext \'95 }Connect your phone to your computer.\ 17 | {\listtext \'95 }Enter the settings menu on your phone, and select 'More...', below 'Wi-Fi' and 'Bluetooth'.\ 18 | {\listtext \'95 }Select 'Hotspot settings'.\ 19 | {\listtext \'95 }Check the 'USB tethering' box.\ 20 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 21 | \cf0 (These instructions were developed on a Galaxy Nexus running Android 4.1; exact steps on your phone may vary.)\ 22 | \ 23 | Your Mac should subsequently detect the device, and connect to the Internet.\ 24 | \ 25 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 26 | \cf0 For further support, please visit {\field{\*\fldinst{HYPERLINK "http://www.joshuawise.com/horndis"}}{\fldrslt http://www.joshuawise.com/horndis}}.} -------------------------------------------------------------------------------- /package/scripts/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Cleans-up after the old HoRDNIS installations. 3 | # These would install into both /Library/Extensions and 4 | # /System/Library/Extensions, while we only use the former location. 5 | sudo rm -rf /System/Library/Extensions/HoRNDIS.kext 6 | sudo touch /System/Library/Extensions 7 | -------------------------------------------------------------------------------- /test_kext.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Variables 4 | SOURCE="${BASH_SOURCE[0]}" 5 | while [ -h "$SOURCE" ]; do DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )";SOURCE="$(readlink "$SOURCE")";[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"; done 6 | CURDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 7 | 8 | DESTDIR="/tmp/HoRNDIS"; 9 | 10 | 11 | # Get sudo 12 | if [ -z $(sudo echo "gotsudo") ]; then echo "Could not get super-user permissions"; exit -1; fi 13 | 14 | 15 | # Copy KEXT to tmp 16 | echo "Copying..."; 17 | 18 | if [ -d $DESTDIR ]; then sudo rm -r $DESTDIR; fi 19 | sudo mkdir $DESTDIR; 20 | sudo cp -r $CURDIR/HoRNDIS* $DESTDIR/; #Copies all files beginning with HoRNDIS… 21 | sudo chmod -R 0700 $DESTDIR; 22 | sudo chown -R root:wheel $DESTDIR; 23 | 24 | # Load KEXT 25 | 26 | KEXTFILES=(); 27 | while read -r -d $'\0'; do KEXTFILES+=("$REPLY"); done < <(sudo find $DESTDIR -iname "*.kext" -maxdepth 1 -print0); 28 | if [ ${#KEXTFILES[@]} -gt 1 ]; then 29 | # Ask user which kext to take 30 | NUM_KEXTS=${#KEXTFILES[@]}; 31 | 32 | for (( i=0; i < $NUM_KEXTS; i++ )); do 33 | echo "$i: ${KEXTFILES[$i]}"; 34 | done 35 | 36 | KEXTNUM=-1; 37 | while [ $KEXTNUM -lt 0 ] || [ $KEXTNUM -ge $NUM_KEXTS ]; do read -p "Which file?: " KEXTNUM; done 38 | 39 | # Got file 40 | KEXTFILE=${KEXTFILES[$KEXTNUM]}; 41 | else 42 | KEXTFILE=${KEXTFILES[0]}; 43 | fi 44 | 45 | if [ -n "$KEXTFILE" ]; then 46 | # Load KEXT if found 47 | echo "Loading \"$KEXTFILE\"..."; 48 | sudo kextload -t "$KEXTFILE"; 49 | 50 | echo "Loaded:"; 51 | echo $(kextstat | grep HoRNDIS); 52 | 53 | exit 0; 54 | else 55 | # No KEXT found 56 | echo 'No KEXTFILE found.'; 57 | exit 1; 58 | fi 59 | --------------------------------------------------------------------------------