├── .gitignore ├── 2022网络安全课程设计报告.doc ├── README.md ├── src ├── LICENSE ├── Makefile ├── cmd │ ├── Makefile │ ├── include │ │ └── contact.h │ ├── kernel.c │ └── main.c ├── common │ ├── exchange.c │ ├── helper.c │ ├── include │ │ └── common.h │ └── tools.c └── kernel_mod │ ├── Makefile │ ├── helpers │ ├── app_helper.c │ ├── conn_helper.c │ ├── log_helper.c │ ├── nat_helper.c │ ├── netlink_helper.c │ └── rule_helper.c │ ├── hooks │ ├── hook_main.c │ └── hook_nat.c │ ├── include │ ├── dependency.h │ ├── helper.h │ ├── hook.h │ └── tools.h │ ├── mod_main.c │ └── tools.c └── 参考资料 ├── Netfilter-Firewall_README.md ├── RJFireWall_README.md ├── fix_Netfilter-Firewall_README.md ├── hping3功能以及参数介绍_一只青木呀-CSDN博客_hping3.pdf ├── images ├── Snipaste_2022-10-31_15-33-43.png ├── clip_image002-16414060761671.jpg ├── clip_image002-16414063769822.jpg ├── clip_image002-16414068716543.jpg ├── clip_image002-16414068912734.jpg ├── clip_image002-16414069119185.jpg ├── clip_image002-16414069356946.jpg ├── clip_image002-16414069505887.jpg ├── clip_image002-16414069922098.jpg ├── clip_image002-16414070055289.jpg ├── clip_image002-164140710634010.jpg ├── clip_image002-164140716212611.jpg ├── clip_image002-164140717434112.jpg ├── clip_image002-164140719545413.jpg ├── clip_image002-164140721398714.jpg ├── clip_image002.jpg ├── clip_image002.png └── clip_image004.jpg ├── imgs ├── 0001_tcp处理流程.png ├── 0002_Netfilter框架.png └── 0003_内核交互.png ├── 别人的代码_nltest ├── Makefile ├── nltest_kmod.c └── nltest_uapp.c ├── 深入linux网络核心堆栈.docx └── 老师给的样例代码_my_firewall ├── Makefile ├── my_dev.c └── my_firewall.c /.gitignore: -------------------------------------------------------------------------------- 1 | 大文件 -------------------------------------------------------------------------------- /2022网络安全课程设计报告.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/2022网络安全课程设计报告.doc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 本实验代码修改自[https://github.com/RicheyJang/RJFireWall](https://github.com/RicheyJang/RJFireWall),报告修改自[https://github.com/DAchilles/Netfilter-Firewall](https://github.com/DAchilles/Netfilter-Firewall)。 2 | 3 | # 使用方式 4 | 5 | 即演示时输入的指令: 6 | 7 | ## 1 规则过滤 8 | ```bash 9 | ./fwtest rule add #加一条drop 127.0.0.1 10 | sudo hping3 127.0.0.1 #测试TCP(都不行) 11 | sudo hping3 127.0.0.1 -1 #测试ICMP(都不行) 12 | ``` 13 | 14 | ## 2 规则维护 15 | ```bash 16 | ./fwtest rule ls #展示规则 17 | ./fwtest rule change #改成只禁TCP 18 | ./fwtest rule ls #再次展示规则 19 | sudo hping3 127.0.0.1 #测试TCP(不行) 20 | sudo hping3 127.0.0.1 -1 #测试ICMP(行) 21 | ``` 22 | 23 | ## 3 默认动作 24 | ```bash 25 | ifconfig #找到自己的ip 26 | ping 192.168.217.130 #ping自己(能ping通) 27 | ./fwtest rule default drop 28 | ping 192.168.217.130 #ping自己(不能ping通) 29 | ./fwtest rule default accept 30 | ping 192.168.217.130 #ping自己(恢复能ping通) 31 | ``` 32 | 33 | ## 4 连接管理 34 | ```bash 35 | # 右键文件夹新建终端 36 | # 在第一个终端 37 | sudo hping3 127.0.0.1 -1 #测试ICMP(行) 38 | # 在第二个终端(快速!) 39 | ./fwtest ls connect #显示连接表信息(有信息) 40 | # 第一个终端停止ping,等一会儿 41 | # 在第二个终端 42 | ./fwtest ls connect #显示连接表信息(显示无连接) 43 | ``` 44 | 45 | ## 5 状态检测 46 | ```bash 47 | sudo hping3 127.0.0.1 #非法状态TCP 48 | ./fwtest ls log #显示非法状态被DROP的条目 49 | sudo hping3 127.0.0.1 -1 #非法状态TCP 50 | # 在第二个终端(快速!) 51 | ./fwtest ls connect #显示连接表信息(显示合法状态在状态表中时Establish) 52 | ``` 53 | 54 | ## 6 日志审计 55 | ```bash 56 | ./fwtest ls log > log.txt #能够保存日志 57 | #双击打开log.txt文件 58 | #用ctrl+f对日志进行查询 59 | ``` 60 | 61 | ## 7 NAT转换 62 | ```bash 63 | ifconfig #查看当前ip地址 64 | # 切换主机 65 | ipconfig #看主机的ip地址 66 | ./fwtest nat add #添加一条当前ip地址映射到192.168.80.121的 67 | sudo hping3 192.168.202.249 -1 #开始ping 192.168.202.249 68 | # 另一个终端 69 | sudo tcpdump #开启监听(可以看到来自NAT结果的发包) 70 | ``` 71 | 72 | ## 8 基本界面 73 | 非常友好、简洁、直白、明确,有提示信息 74 | 75 | ## 9 遇到的问题 76 | 1. 规则维护的“修改”比较难设计, 77 | 我希望不用重复输入,但又想复用“添加”规则的代码, 78 | 所以设计成输入特定值代表不修改规则。 79 | 80 | > 此处是由于原来的代码没有规则修改功能,不能照抄,必须改改。 81 | 82 | 2. NAT的时候,我发现删除后NAT的影响没有消除, 83 | 后来仔细一看代码发现只有一条规则的时候, 84 | 消除影响的代码没有被调用, 85 | 这是常见的有表头的链表没有做额外处理导致的错误。 86 | 就像新建链表的时候需要对表头节点做额外的处理,容易忘。 87 | 88 | > 此处是原来的代码的bug。 -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = cmd kernel_mod 2 | 3 | .PHONY:all install clean 4 | 5 | all: 6 | @for subdir in $(SUBDIRS); do\ 7 | $(MAKE) -C $$subdir; \ 8 | done 9 | 10 | install: 11 | @for subdir in $(SUBDIRS); do\ 12 | $(MAKE) -C $$subdir install; \ 13 | done 14 | 15 | clean: 16 | @for subdir in $(SUBDIRS); do\ 17 | $(MAKE) -C $$subdir clean; \ 18 | done -------------------------------------------------------------------------------- /src/cmd/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := fwtest 2 | INCLUDES := -I. -Iinclude -I../common/include 3 | SRCS = ../common/exchange.c ../common/tools.c ../common/helper.c kernel.c main.c 4 | CC := gcc 5 | OBJS = $(SRCS:.c=.o) 6 | 7 | $(TARGET):$(OBJS) 8 | $(CC) -o $@ $^ 9 | rm -rf $(SRCS:.c=.o) 10 | %.o:%.c 11 | $(CC) $(INCLUDES) -o $@ -c $< 12 | 13 | install: 14 | cp $(TARGET) .. 15 | 16 | clean: 17 | rm -rf $(basename $(TARGET)) $(SRCS:.c=.o) -------------------------------------------------------------------------------- /src/cmd/include/contact.h: -------------------------------------------------------------------------------- 1 | #ifndef _HELPER_H 2 | #define _HELPER_H 3 | 4 | #include "common.h" 5 | 6 | void dealResponseAtCmd(struct KernelResponse rsp); 7 | 8 | #endif -------------------------------------------------------------------------------- /src/cmd/kernel.c: -------------------------------------------------------------------------------- 1 | #include "contact.h" 2 | 3 | int showRules(struct IPRule *rules, int len); 4 | int showNATRules(struct NATRecord *rules, int len); 5 | int showLogs(struct IPLog *logs, int len); 6 | int showConns(struct ConnLog *logs, int len); 7 | 8 | void dealResponseAtCmd(struct KernelResponse rsp) { 9 | // 判断错误码 10 | switch (rsp.code) { 11 | case ERROR_CODE_EXIT: 12 | exit(0); 13 | break; 14 | case ERROR_CODE_NO_SUCH_RULE: 15 | printf("no such rule.\n"); 16 | return; 17 | case ERROR_CODE_WRONG_IP: 18 | printf("Incorrect IP format.\n"); 19 | return; 20 | } 21 | if(rsp.code < 0 || rsp.data == NULL || rsp.header == NULL || rsp.body == NULL) 22 | return; 23 | // 处理数据 24 | switch (rsp.header->bodyTp) { 25 | case RSP_Only_Head: 26 | printf("succeed to delete %d rules.\n", rsp.header->arrayLen); 27 | break; 28 | case RSP_MSG: 29 | printf("From kernel: %s\n", (char*)rsp.body); 30 | break; 31 | case RSP_IPRules: 32 | showRules((struct IPRule*)rsp.body, rsp.header->arrayLen); 33 | break; 34 | case RSP_NATRules: 35 | showNATRules((struct NATRecord*)rsp.body, rsp.header->arrayLen); 36 | break; 37 | case RSP_IPLogs: 38 | showLogs((struct IPLog*)rsp.body, rsp.header->arrayLen); 39 | break; 40 | case RSP_ConnLogs: 41 | showConns((struct ConnLog*)rsp.body, rsp.header->arrayLen); 42 | break; 43 | } 44 | if(rsp.header->bodyTp != RSP_Only_Head && rsp.body != NULL) { 45 | free(rsp.data); 46 | } 47 | } 48 | 49 | void printLine(int len) { 50 | int i; 51 | for(i = 0; i < len; i++) { 52 | printf("-"); 53 | } 54 | printf("\n"); 55 | } 56 | 57 | int showOneRule(struct IPRule rule) { 58 | char saddr[25],daddr[25],sport[13],dport[13],proto[6],action[8],log[5]; 59 | // ip 60 | IPint2IPstr(rule.saddr,rule.smask,saddr); 61 | IPint2IPstr(rule.daddr,rule.dmask,daddr); 62 | // port 63 | if(rule.sport == 0xFFFFu) 64 | strcpy(sport, "any"); 65 | else if((rule.sport >> 16) == (rule.sport & 0xFFFFu)) 66 | sprintf(sport, "only %u", (rule.sport >> 16)); 67 | else 68 | sprintf(sport, "%u~%u", (rule.sport >> 16), (rule.sport & 0xFFFFu)); 69 | if(rule.dport == 0xFFFFu) 70 | strcpy(dport, "any"); 71 | else if((rule.dport >> 16) == (rule.dport & 0xFFFFu)) 72 | sprintf(dport, "only %u", (rule.dport >> 16)); 73 | else 74 | sprintf(dport, "%u~%u", (rule.dport >> 16), (rule.dport & 0xFFFFu)); 75 | // action 76 | if(rule.action == NF_ACCEPT) { 77 | sprintf(action, "accept"); 78 | } else if(rule.action == NF_DROP) { 79 | sprintf(action, "drop"); 80 | } else { 81 | sprintf(action, "other"); 82 | } 83 | // protocol 84 | if(rule.protocol == IPPROTO_TCP) { 85 | sprintf(proto, "TCP"); 86 | } else if(rule.protocol == IPPROTO_UDP) { 87 | sprintf(proto, "UDP"); 88 | } else if(rule.protocol == IPPROTO_ICMP) { 89 | sprintf(proto, "ICMP"); 90 | } else if(rule.protocol == IPPROTO_IP) { 91 | sprintf(proto, "IP"); 92 | } else { 93 | sprintf(proto, "other"); 94 | } 95 | // log 96 | if(rule.log) { 97 | sprintf(log, "yes"); 98 | } else { 99 | sprintf(log, "no"); 100 | } 101 | // print 102 | printf("| %-*s | %-18s | %-18s | %-11s | %-11s | %-8s | %-6s | %-3s |\n", MAXRuleNameLen, 103 | rule.name, saddr, daddr, sport, dport, proto, action, log); 104 | printLine(111); 105 | } 106 | 107 | int showRules(struct IPRule *rules, int len) { 108 | int i; 109 | if(len == 0) { 110 | printf("No rules now.\n"); 111 | return 0; 112 | } 113 | //printf("rule num: %d\n", len); 114 | printLine(111); 115 | printf("| %-*s | %-18s | %-18s | %-11s | %-11s | %-8s | %-6s | %-3s |\n", MAXRuleNameLen, 116 | "name", "source ip", "target ip", "source port", "target port", "protocol", "action", "log"); 117 | printLine(111); 118 | for(i = 0; i < len; i++) { 119 | showOneRule(rules[i]); 120 | } 121 | return 0; 122 | } 123 | 124 | int showNATRules(struct NATRecord *rules, int len) { 125 | int i, col = 66; 126 | char saddr[25],daddr[25]; 127 | if(len == 0) { 128 | printf("No NAT rules now.\n"); 129 | return 0; 130 | } 131 | //printf("NAT rule num: %d\n", len); 132 | printLine(col); 133 | printf("| seq | %18s |->| %-18s | %-11s |\n", "source ip", "NAT ip", "NAT port"); 134 | printLine(col); 135 | for(i = 0; i < len; i++) { 136 | IPint2IPstr(rules[i].saddr,rules[i].smask,saddr); 137 | IPint2IPstrNoMask(rules[i].daddr,daddr); 138 | printf("| %3d | %18s |->| %-18s | %5u~%-5u |\n", i, saddr, daddr, rules[i].sport, rules[i].dport); 139 | printLine(col); 140 | } 141 | return 0; 142 | } 143 | 144 | int showOneLog(struct IPLog log) { 145 | struct tm * timeinfo; 146 | char saddr[25],daddr[25],proto[6],action[8],tm[21]; 147 | // ip 148 | IPint2IPstrWithPort(log.saddr, log.sport, saddr); 149 | IPint2IPstrWithPort(log.daddr, log.dport, daddr); 150 | // action 151 | if(log.action == NF_ACCEPT) { 152 | sprintf(action, "[ACCEPT]"); 153 | } else if(log.action == NF_DROP) { 154 | sprintf(action, "[DROP]"); 155 | } else { 156 | sprintf(action, "[unknown]"); 157 | } 158 | // protocol 159 | if(log.protocol == IPPROTO_TCP) { 160 | sprintf(proto, "TCP"); 161 | } else if(log.protocol == IPPROTO_UDP) { 162 | sprintf(proto, "UDP"); 163 | } else if(log.protocol == IPPROTO_ICMP) { 164 | sprintf(proto, "ICMP"); 165 | } else if(log.protocol == IPPROTO_IP) { 166 | sprintf(proto, "IP"); 167 | } else { 168 | sprintf(proto, "other"); 169 | } 170 | // time 171 | timeinfo = localtime(&log.tm); 172 | sprintf(tm, "%4d-%02d-%02d %02d:%02d:%02d", 173 | 1900 + timeinfo->tm_year, 1 + timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); 174 | // print 175 | printf("[%s] %-9s %s->%s proto=%s len=%uB\n", 176 | tm, action, saddr, daddr, proto, log.len); 177 | } 178 | 179 | int showLogs(struct IPLog *logs, int len) { 180 | int i; 181 | if(len == 0) { 182 | printf("No logs now.\n"); 183 | return 0; 184 | } 185 | printf("sum: %d\n", len); 186 | for(i = 0; i < len; i++) { 187 | showOneLog(logs[i]); 188 | } 189 | return 0; 190 | } 191 | 192 | int showOneConn(struct ConnLog log) { 193 | struct tm * timeinfo; 194 | char saddr[25],daddr[25],proto[6]; 195 | // ip 196 | IPint2IPstrWithPort(log.saddr,log.sport,saddr); 197 | IPint2IPstrWithPort(log.daddr,log.dport,daddr); 198 | // protocol 199 | if(log.protocol == IPPROTO_TCP) { 200 | sprintf(proto, "TCP"); 201 | } else if(log.protocol == IPPROTO_UDP) { 202 | sprintf(proto, "UDP"); 203 | } else if(log.protocol == IPPROTO_ICMP) { 204 | sprintf(proto, "ICMP"); 205 | } else if(log.protocol == IPPROTO_IP) { 206 | sprintf(proto, "any"); 207 | } else { 208 | sprintf(proto, "other"); 209 | } 210 | printf("| %-5s | %21s |->| %21s | Established |\n",proto, saddr, daddr); 211 | if(log.natType == NAT_TYPE_SRC) { 212 | IPint2IPstrWithPort(log.nat.daddr, log.nat.dport, saddr); 213 | printf("| %-5s |=>%21s |->| %21c | %11c |\n", "NAT", saddr, ' ', ' '); 214 | } else if(log.natType == NAT_TYPE_DEST) { 215 | IPint2IPstrWithPort(log.nat.daddr, log.nat.dport, daddr); 216 | printf("| %-5s | %21c |->|=>%21s | %11c |\n", "NAT", ' ', daddr, ' '); 217 | } 218 | //printLine(80); 219 | } 220 | 221 | int showConns(struct ConnLog *logs, int len) { 222 | int i, col = 78; 223 | if(len == 0) { 224 | printf("No connections now.\n"); 225 | return 0; 226 | } 227 | printf("connection num: %d\n", len); 228 | printLine(col); 229 | printf("| %-5s | %21s |->| %21s | %11s |\n", "proto", "source addr", "dest addr", "status"); 230 | printLine(col); 231 | for(i = 0; i < len; i++) { 232 | showOneConn(logs[i]); 233 | } 234 | printLine(col); 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /src/cmd/main.c: -------------------------------------------------------------------------------- 1 | #include "contact.h" 2 | 3 | // 新增过滤规则时的用户交互 4 | struct KernelResponse cmdAddRule() { 5 | struct KernelResponse empty; 6 | char after[MAXRuleNameLen+1],name[MAXRuleNameLen+1],saddr[25],daddr[25],sport[15],dport[15],protoS[6]; 7 | unsigned short sportMin,sportMax,dportMin,dportMax; 8 | unsigned int action = NF_DROP, log = 0, proto, i; 9 | empty.code = ERROR_CODE_EXIT; 10 | // 前序规则名 11 | printf("add rule after [enter for adding at head]: "); 12 | for(i=0;;i++) { 13 | if(i>MAXRuleNameLen) { 14 | printf("name too long.\n"); 15 | return empty; 16 | } 17 | after[i] = getchar(); 18 | if(after[i] == '\n' || after[i] == '\r') { 19 | after[i] = '\0'; 20 | break; 21 | } 22 | } 23 | // 规则名 24 | printf("rule name [max len=%d]: ", MAXRuleNameLen); 25 | scanf("%s",name); 26 | if(strlen(name)==0 || strlen(name)>MAXRuleNameLen) { 27 | printf("name too long or too short.\n"); 28 | return empty; 29 | } 30 | // 源IP 31 | printf("source ip and mask [like 127.0.0.1/16]: "); 32 | scanf("%s",saddr); 33 | // 源端口 34 | printf("source port range [like 8080-8031 or any]: "); 35 | scanf("%s",sport); 36 | if(strcmp(sport, "any") == 0) { 37 | sportMin = 0,sportMax = 0xFFFFu; 38 | } else { 39 | sscanf(sport,"%hu-%hu",&sportMin,&sportMax); 40 | } 41 | if(sportMin > sportMax) { 42 | printf("the min port > max port.\n"); 43 | return empty; 44 | } 45 | // 目的IP 46 | printf("target ip and mask [like 127.0.0.1/16]: "); 47 | scanf("%s",daddr); 48 | // 目的端口 49 | printf("target port range [like 8080-8031 or any]: "); 50 | scanf("%s",dport); 51 | if(strcmp(dport, "any") == 0) { 52 | dportMin = 0,dportMax = 0xFFFFu; 53 | } else { 54 | sscanf(dport,"%hu-%hu",&dportMin,&dportMax); 55 | } 56 | if(dportMin > dportMax) { 57 | printf("the min port > max port.\n"); 58 | return empty; 59 | } 60 | // 协议 61 | printf("protocol [TCP/UDP/ICMP/any]: "); 62 | scanf("%s",protoS); 63 | if(strcmp(protoS,"TCP")==0) 64 | proto = IPPROTO_TCP; 65 | else if(strcmp(protoS,"UDP")==0) 66 | proto = IPPROTO_UDP; 67 | else if(strcmp(protoS,"ICMP")==0) 68 | proto = IPPROTO_ICMP; 69 | else if(strcmp(protoS,"any")==0) 70 | proto = IPPROTO_IP; 71 | else { 72 | printf("This protocol is not supported.\n"); 73 | return empty; 74 | } 75 | // 动作 76 | printf("action [1 for accept,0 for drop]: "); 77 | scanf("%d",&action); 78 | // 是否记录日志 79 | printf("is log [1 for yes,0 for no]: "); 80 | scanf("%u",&log); 81 | printf("result:\n"); 82 | return addFilterRule(after,name,saddr,daddr, 83 | (((unsigned int)sportMin << 16) | (((unsigned int)sportMax) & 0xFFFFu)), 84 | (((unsigned int)dportMin << 16) | (((unsigned int)dportMax) & 0xFFFFu)),proto,log,action); 85 | } 86 | 87 | // 修改过滤规则时的用户交互 88 | struct KernelResponse cmdChangeRule() { 89 | struct KernelResponse empty; 90 | char name[MAXRuleNameLen+1],saddr[25],daddr[25],sport[15],dport[15],protoS[6]; 91 | unsigned short sportMin,sportMax,dportMin,dportMax; 92 | unsigned int action = NF_DROP, log = 0, proto, i, key = 0; 93 | empty.code = ERROR_CODE_EXIT; 94 | // 规则序号(以1开始) 95 | printf("order of the rule to be changed [like 1 for the head]: "); 96 | scanf("%d", &key); 97 | // 规则名 98 | printf("rule name [max len=%d, -1 for no change]: ", MAXRuleNameLen); 99 | scanf("%s",name); 100 | if(strlen(name)==0 || strlen(name)>MAXRuleNameLen) { 101 | printf("name too long or too short.\n"); 102 | return empty; 103 | } 104 | // 源IP 105 | printf("source ip and mask [like 127.0.0.1/16, -1 for no change]: "); 106 | scanf("%s",saddr); 107 | // 源端口 108 | printf("source port range [like 8080-8031 or any, -1 for no change]: "); 109 | scanf("%s",sport); 110 | if(strcmp(sport, "-1") == 0){ 111 | sportMin = 0, sportMax= 0; 112 | } else if(strcmp(sport, "any") == 0) { 113 | sportMin = 0,sportMax = 0xFFFFu; 114 | } else { 115 | sscanf(sport,"%hu-%hu",&sportMin,&sportMax); 116 | } 117 | if(sportMin > sportMax) { 118 | printf("the min port > max port.\n"); 119 | return empty; 120 | } 121 | // 目的IP 122 | printf("target ip and mask [like 127.0.0.1/16, -1 for no change]: "); 123 | scanf("%s",daddr); 124 | // 目的端口 125 | printf("target port range [like 8080-8031 or any, -1 for no change]: "); 126 | scanf("%s",dport); 127 | if(strcmp(sport, "-1") == 0){ 128 | dportMin=0,dportMax=0; 129 | } else if(strcmp(dport, "any") == 0) { 130 | dportMin = 0,dportMax = 0xFFFFu; 131 | } else { 132 | sscanf(dport,"%hu-%hu",&dportMin,&dportMax); 133 | } 134 | if(dportMin > dportMax) { 135 | printf("the min port > max port.\n"); 136 | return empty; 137 | } 138 | // 协议 139 | printf("protocol [TCP/UDP/ICMP/any, -1 for no change]: "); 140 | scanf("%s",protoS); 141 | if(strcmp(protoS,"TCP")==0) 142 | proto = IPPROTO_TCP; 143 | else if(strcmp(protoS,"UDP")==0) 144 | proto = IPPROTO_UDP; 145 | else if(strcmp(protoS,"ICMP")==0) 146 | proto = IPPROTO_ICMP; 147 | else if(strcmp(protoS,"any")==0) 148 | proto = IPPROTO_IP; 149 | else if(strcmp(protoS, "-1")==0) 150 | proto = 255; 151 | else { 152 | printf("This protocol is not supported.\n"); 153 | return empty; 154 | } 155 | // 动作 156 | printf("action [1 for accept, 0 for drop, 2 for no change]: "); 157 | scanf("%d",&action); 158 | // 是否记录日志 159 | printf("is log [1 for yes, 0 for no, 2 for no change]: "); 160 | scanf("%u",&log); 161 | printf("result:\n"); 162 | return changeFilterRule(key,name,saddr,daddr, 163 | (((unsigned int)sportMin << 16) | (((unsigned int)sportMax) & 0xFFFFu)), 164 | (((unsigned int)dportMin << 16) | (((unsigned int)dportMax) & 0xFFFFu)),proto,log,action); 165 | } 166 | 167 | struct KernelResponse cmdAddNATRule() { 168 | struct KernelResponse empty; 169 | char saddr[25],daddr[25],port[15]; 170 | unsigned short portMin,portMax; 171 | empty.code = ERROR_CODE_EXIT; 172 | printf("ONLY source NAT is supported\n"); 173 | // 源IP 174 | printf("source ip and mask [like 127.0.0.1/16]: "); 175 | scanf("%s",saddr); 176 | // NAT IP 177 | printf("NAT ip [like 192.168.80.139]: "); 178 | scanf("%s",daddr); 179 | // 目的端口 180 | printf("NAT port range [like 10000-30000 or any]: "); 181 | scanf("%s",port); 182 | if(strcmp(port, "any") == 0) { 183 | portMin = 0,portMax = 0xFFFFu; 184 | } else { 185 | sscanf(port,"%hu-%hu",&portMin,&portMax); 186 | } 187 | if(portMin > portMax) { 188 | printf("the min port > max port.\n"); 189 | return empty; 190 | } 191 | return addNATRule(saddr,daddr,portMin,portMax); 192 | } 193 | 194 | void wrongCommand() { 195 | printf("wrong command.\n"); 196 | printf("uapp [option]\n"); 197 | printf("commands: rule [del rule's name]\n"); 198 | printf(" nat [del number]\n"); 199 | printf(" ls \n"); 200 | exit(0); 201 | } 202 | 203 | int main(int argc, char *argv[]) { 204 | if(argc<3) { // SSD: 3个或以上参数 205 | //addRule("","rj","192.168.80.138","47.100.10.21",-1,-1,IPPROTO_ICMP,1,NF_DROP); 206 | //addNATRule("192.168.60.2", "192.168.80.139",20000,35535); 207 | wrongCommand(); 208 | return 0; 209 | } 210 | struct KernelResponse rsp; 211 | rsp.code = ERROR_CODE_EXIT; 212 | // 过滤规则相关 213 | if(strcmp(argv[1], "rule")==0 || argv[1][0] == 'r') { 214 | if(strcmp(argv[2], "ls")==0 || strcmp(argv[2], "list")==0) { 215 | // 列出所有过滤规则 216 | rsp = getAllFilterRules(); 217 | } else if(strcmp(argv[2], "del")==0) { 218 | // 删除过滤规则 219 | if(argc < 4) 220 | printf("Please point rule name in option.\n"); 221 | else if(strlen(argv[3])>MAXRuleNameLen) 222 | printf("rule name too long!"); 223 | else 224 | rsp = delFilterRule(argv[3]); 225 | } else if(strcmp(argv[2], "add")==0) { 226 | // 添加过滤规则 227 | rsp = cmdAddRule(); 228 | } else if(strcmp(argv[2], "change")==0) { 229 | // 修改指定规则 230 | rsp = cmdChangeRule(); 231 | } else if(strcmp(argv[2], "default")==0) { 232 | // 设置默认规则 233 | if(argc < 4) 234 | printf("Please point default action in option.\n"); 235 | else if(strcmp(argv[3], "accept")==0) 236 | rsp = setDefaultAction(NF_ACCEPT); 237 | else if(strcmp(argv[3], "drop")==0) 238 | rsp = setDefaultAction(NF_DROP); 239 | else 240 | printf("No such action. Only \"accept\" or \"drop\".\n"); 241 | } else 242 | wrongCommand(); 243 | } else if(strcmp(argv[1], "nat")==0 || argv[1][0] == 'n') { 244 | if(strcmp(argv[2], "ls")==0 || strcmp(argv[2], "list")==0) { 245 | // 列出所有NAT规则 246 | rsp = getAllNATRules(); 247 | } else if(strcmp(argv[2], "del")==0) { 248 | // 删除NAT规则 249 | if(argc < 4) 250 | printf("Please point rule number(seq) in option.\n"); 251 | else { 252 | int num; 253 | sscanf(argv[3], "%d", &num); 254 | rsp = delNATRule(num); 255 | } 256 | } else if(strcmp(argv[2], "add")==0) { 257 | // 添加NAT规则 258 | rsp = cmdAddNATRule(); 259 | } else { 260 | wrongCommand(); 261 | } 262 | } else if(strcmp(argv[1], "ls")==0 || argv[1][0] == 'l') { 263 | // 展示相关 264 | if(strcmp(argv[2],"log")==0 || argv[2][0] == 'l') { 265 | // 过滤日志 266 | unsigned int num = 0; 267 | if(argc > 3) 268 | sscanf(argv[3], "%u", &num); 269 | rsp = getLogs(num); 270 | } else if(strcmp(argv[2],"con")==0 || argv[2][0] == 'c') { 271 | // 连接状态 272 | rsp = getAllConns(); 273 | } else if(strcmp(argv[2],"rule")==0 || argv[2][0] == 'r') { 274 | // 已有过滤规则 275 | rsp = getAllFilterRules(); 276 | } else if(strcmp(argv[2],"nat")==0 || argv[2][0] == 'n') { 277 | // 已有NAT规则 278 | rsp = getAllNATRules(); 279 | } else 280 | wrongCommand(); 281 | } else 282 | wrongCommand(); 283 | dealResponseAtCmd(rsp); 284 | } 285 | -------------------------------------------------------------------------------- /src/common/exchange.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | // 4 | struct KernelResponse exchangeMsgK(void *smsg, unsigned int slen) 5 | { 6 | struct sockaddr_nl local; 7 | struct sockaddr_nl kpeer; 8 | struct KernelResponse rsp; 9 | int dlen, kpeerlen = sizeof(struct sockaddr_nl); 10 | // init socket - skfd 11 | int skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_MYFW); 12 | if (skfd < 0) 13 | { 14 | // printf("[exchangeMsgK] can not create a netlink socket\n"); 15 | rsp.code = ERROR_CODE_EXCHANGE; 16 | return rsp; 17 | } 18 | // bind - skfd and local 19 | memset(&local, 0, sizeof(local)); 20 | local.nl_family = AF_NETLINK; 21 | local.nl_pid = getpid(); 22 | local.nl_groups = 0; 23 | if (bind(skfd, (struct sockaddr *)&local, sizeof(local)) != 0) 24 | { 25 | // printf("[exchangeMsgK] bind() error\n"); 26 | close(skfd); 27 | rsp.code = ERROR_CODE_EXCHANGE; 28 | return rsp; 29 | } 30 | 31 | memset(&kpeer, 0, sizeof(kpeer)); 32 | kpeer.nl_family = AF_NETLINK; 33 | kpeer.nl_pid = 0; 34 | kpeer.nl_groups = 0; 35 | // set send msg 36 | struct nlmsghdr *message = (struct nlmsghdr *)malloc(NLMSG_SPACE(slen) * sizeof(uint8_t)); 37 | if (!message) 38 | { 39 | // printf("[exchangeMsgK] malloc fail"); 40 | close(skfd); 41 | rsp.code = ERROR_CODE_EXCHANGE; 42 | return rsp; 43 | } 44 | memset(message, '\0', sizeof(struct nlmsghdr)); 45 | message->nlmsg_len = NLMSG_SPACE(slen); 46 | message->nlmsg_flags = 0; 47 | message->nlmsg_type = 0; 48 | message->nlmsg_seq = 0; 49 | message->nlmsg_pid = local.nl_pid; 50 | memcpy(NLMSG_DATA(message), smsg, slen); 51 | // send msg 52 | if (!sendto(skfd, message, message->nlmsg_len, 0, (struct sockaddr *)&kpeer, sizeof(kpeer))) 53 | { 54 | // printf("[exchangeMsgK] sendto fail");ujj 55 | close(skfd); 56 | free(message); 57 | rsp.code = ERROR_CODE_EXCHANGE; 58 | return rsp; 59 | } 60 | // recv msg 61 | struct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD) * sizeof(uint8_t)); 62 | if (!nlh) 63 | { 64 | // printf("[exchangeMsgK] nlh malloc fail"); 65 | close(skfd); 66 | free(message); 67 | rsp.code = ERROR_CODE_EXCHANGE; 68 | return rsp; 69 | } 70 | if (!recvfrom(skfd, nlh, NLMSG_SPACE(MAX_PAYLOAD), 0, (struct sockaddr *)&kpeer, (socklen_t *)&kpeerlen)) 71 | { 72 | // printf("[exchangeMsgK] recvfrom fail"); 73 | close(skfd); 74 | free(message); 75 | free(nlh); 76 | rsp.code = ERROR_CODE_EXCHANGE; 77 | return rsp; 78 | } 79 | dlen = nlh->nlmsg_len - NLMSG_SPACE(0); 80 | rsp.data = malloc(dlen); 81 | if (!(rsp.data)) 82 | { 83 | // printf("[exchangeMsgK] dmsg malloc fail"); 84 | close(skfd); 85 | free(message); 86 | free(nlh); 87 | rsp.code = ERROR_CODE_EXCHANGE; 88 | return rsp; 89 | } 90 | memset(rsp.data, 0, dlen); 91 | memcpy(rsp.data, NLMSG_DATA(nlh), dlen); 92 | rsp.code = dlen - sizeof(struct KernelResponseHeader); 93 | if (rsp.code < 0) 94 | { 95 | rsp.code = ERROR_CODE_EXCHANGE; 96 | } 97 | rsp.header = (struct KernelResponseHeader *)rsp.data; 98 | rsp.body = rsp.data + sizeof(struct KernelResponseHeader); 99 | // over 100 | close(skfd); 101 | free(message); 102 | free(nlh); 103 | return rsp; 104 | } -------------------------------------------------------------------------------- /src/common/helper.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | struct KernelResponse addFilterRule(char *after,char *name,char *sip,char *dip,unsigned int sport,unsigned int dport,u_int8_t proto,unsigned int log,unsigned int action) { 4 | struct APPRequest req; 5 | struct KernelResponse rsp; 6 | // form rule 7 | struct IPRule rule; 8 | if(IPstr2IPint(sip,&rule.saddr,&rule.smask)!=0) { 9 | rsp.code = ERROR_CODE_WRONG_IP; 10 | return rsp; 11 | } 12 | if(IPstr2IPint(dip,&rule.daddr,&rule.dmask)!=0) { 13 | rsp.code = ERROR_CODE_WRONG_IP; 14 | return rsp; 15 | } 16 | rule.saddr = rule.saddr; 17 | rule.daddr = rule.daddr; 18 | rule.sport = sport; 19 | rule.dport = dport; 20 | rule.log = log; 21 | rule.action = action; 22 | rule.protocol = proto; 23 | strncpy(rule.name, name, MAXRuleNameLen); 24 | // form req 25 | req.tp = REQ_ADDIPRule; 26 | req.ruleName[0]=0; 27 | strncpy(req.ruleName, after, MAXRuleNameLen); 28 | req.msg.ipRule = rule; 29 | // exchange 30 | return exchangeMsgK(&req, sizeof(req)); 31 | } 32 | 33 | struct KernelResponse changeFilterRule(int key, char *name,char *sip,char *dip,unsigned int sport,unsigned int dport,u_int8_t proto,unsigned int log,unsigned int action) { 34 | struct APPRequest req; 35 | struct KernelResponse rsp; 36 | // form rule 37 | struct IPRule rule; 38 | if(strcmp(sip, "-1") == 0) 39 | rule.saddr=0, rule.smask=0; 40 | else if(IPstr2IPint(sip,&rule.saddr,&rule.smask)!=0) { 41 | rsp.code = ERROR_CODE_WRONG_IP; 42 | return rsp; 43 | } 44 | if(strcmp(dip, "-1") == 0) 45 | rule.daddr=0, rule.dmask=0; 46 | else if(IPstr2IPint(dip,&rule.daddr,&rule.dmask)!=0) { 47 | rsp.code = ERROR_CODE_WRONG_IP; 48 | return rsp; 49 | } 50 | rule.sport = sport; 51 | rule.dport = dport; 52 | rule.log = log; 53 | rule.action = action; 54 | rule.protocol = proto; 55 | strncpy(rule.name, name, MAXRuleNameLen); 56 | // form req 57 | req.tp = REQ_CHANGEIPRule; 58 | req.num = key; 59 | req.msg.ipRule = rule; 60 | // exchange 61 | return exchangeMsgK(&req, sizeof(req)); 62 | } 63 | 64 | struct KernelResponse delFilterRule(char *name) { 65 | struct APPRequest req; 66 | // form request 67 | req.tp = REQ_DELIPRule; 68 | strncpy(req.ruleName, name, MAXRuleNameLen); 69 | // exchange 70 | return exchangeMsgK(&req, sizeof(req)); 71 | } 72 | 73 | struct KernelResponse getAllFilterRules(void) { 74 | struct APPRequest req; 75 | // exchange msg 76 | req.tp = REQ_GETAllIPRules; 77 | return exchangeMsgK(&req, sizeof(req)); 78 | } 79 | 80 | struct KernelResponse addNATRule(char *sip,char *natIP,unsigned short minport,unsigned short maxport) { 81 | struct APPRequest req; 82 | struct KernelResponse rsp; 83 | // form rule 84 | struct NATRecord rule; 85 | if(IPstr2IPint(natIP,&rule.daddr,&rule.smask)!=0) { 86 | rsp.code = ERROR_CODE_WRONG_IP; 87 | return rsp; 88 | } 89 | if(IPstr2IPint(sip,&rule.saddr,&rule.smask)!=0) { 90 | rsp.code = ERROR_CODE_WRONG_IP; 91 | return rsp; 92 | } 93 | rule.sport = minport; 94 | rule.dport = maxport; 95 | // form req 96 | req.tp = REQ_ADDNATRule; 97 | req.msg.natRule = rule; 98 | // exchange 99 | return exchangeMsgK(&req, sizeof(req)); 100 | } 101 | 102 | struct KernelResponse delNATRule(int num) { 103 | struct APPRequest req; 104 | struct KernelResponse rsp; 105 | if(num < 0) { 106 | rsp.code = ERROR_CODE_NO_SUCH_RULE; 107 | return rsp; 108 | } 109 | req.tp = REQ_DELNATRule; 110 | req.msg.num = num; 111 | // exchange 112 | return exchangeMsgK(&req, sizeof(req)); 113 | } 114 | 115 | struct KernelResponse getAllNATRules(void) { 116 | struct APPRequest req; 117 | // exchange msg 118 | req.tp = REQ_GETNATRules; 119 | return exchangeMsgK(&req, sizeof(req)); 120 | } 121 | 122 | struct KernelResponse setDefaultAction(unsigned int action) { 123 | struct APPRequest req; 124 | // form request 125 | req.tp = REQ_SETAction; 126 | req.msg.defaultAction = action; 127 | // exchange 128 | return exchangeMsgK(&req, sizeof(req)); 129 | } 130 | 131 | struct KernelResponse getLogs(unsigned int num) { 132 | struct APPRequest req; 133 | // exchange msg 134 | req.msg.num = num; 135 | req.tp = REQ_GETAllIPLogs; 136 | return exchangeMsgK(&req, sizeof(req)); 137 | } 138 | 139 | struct KernelResponse getAllConns(void) { 140 | struct APPRequest req; 141 | // exchange msg 142 | req.tp = REQ_GETAllConns; 143 | return exchangeMsgK(&req, sizeof(req)); 144 | } 145 | -------------------------------------------------------------------------------- /src/common/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_APP_H 2 | #define _COMMON_APP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // ---- APP 与 Kernel 通用协议 ------ 17 | #define MAXRuleNameLen 11 18 | 19 | #define REQ_GETAllIPRules 1 20 | #define REQ_ADDIPRule 2 21 | #define REQ_CHANGEIPRule 16 22 | #define REQ_DELIPRule 3 23 | #define REQ_SETAction 4 24 | #define REQ_GETAllIPLogs 5 25 | #define REQ_GETAllConns 6 26 | #define REQ_ADDNATRule 7 27 | #define REQ_DELNATRule 8 28 | #define REQ_GETNATRules 9 29 | 30 | #define RSP_Only_Head 10 31 | #define RSP_MSG 11 32 | #define RSP_IPRules 12 // body为IPRule[] 33 | #define RSP_IPLogs 13 // body为IPlog[] 34 | #define RSP_NATRules 14 // body为NATRecord[] 35 | #define RSP_ConnLogs 15 // body为ConnLog[] 36 | 37 | struct IPRule { 38 | char name[MAXRuleNameLen+1]; 39 | unsigned int saddr; 40 | unsigned int smask; 41 | unsigned int daddr; 42 | unsigned int dmask; 43 | unsigned int sport; // 源端口范围 高2字节为最小 低2字节为最大 44 | unsigned int dport; // 目的端口范围 同上 45 | u_int8_t protocol; 46 | unsigned int action; 47 | unsigned int log; 48 | struct IPRule* nx; 49 | }; 50 | 51 | struct IPLog { 52 | long tm; 53 | unsigned int saddr; 54 | unsigned int daddr; 55 | unsigned short sport; 56 | unsigned short dport; 57 | u_int8_t protocol; 58 | unsigned int len; 59 | unsigned int action; 60 | struct IPLog* nx; 61 | }; 62 | 63 | struct NATRecord { // NAT 记录 or 规则(源IP端口转换) 64 | unsigned int saddr; // 记录:原始IP | 规则:原始源IP 65 | unsigned int smask; // 记录:无作用 | 规则:原始源IP掩码 66 | unsigned int daddr; // 记录:转换后的IP | 规则:NAT 源IP 67 | 68 | unsigned short sport; // 记录:原始端口 | 规则:最小端口范围 69 | unsigned short dport; // 记录:转换后的端口 | 规则:最大端口范围 70 | unsigned short nowPort; // 记录:当前使用端口 | 规则:无作用 71 | struct NATRecord* nx; 72 | }; 73 | 74 | struct ConnLog { 75 | unsigned int saddr; 76 | unsigned int daddr; 77 | unsigned short sport; 78 | unsigned short dport; 79 | u_int8_t protocol; 80 | int natType; 81 | struct NATRecord nat; // NAT记录 82 | }; 83 | 84 | struct APPRequest { 85 | unsigned int tp; 86 | char ruleName[MAXRuleNameLen+1]; 87 | int num; 88 | union { 89 | struct IPRule ipRule; 90 | struct NATRecord natRule; 91 | unsigned int defaultAction; 92 | unsigned int num; 93 | } msg; 94 | }; 95 | 96 | struct KernelResponseHeader { 97 | unsigned int bodyTp; 98 | unsigned int arrayLen; 99 | }; 100 | 101 | #define NAT_TYPE_NO 0 102 | #define NAT_TYPE_SRC 1 103 | #define NAT_TYPE_DEST 2 104 | 105 | // ----- 上层应用专用 ------ 106 | #define uint8_t unsigned char 107 | #define NETLINK_MYFW 17 108 | #define MAX_PAYLOAD (1024 * 256) 109 | 110 | #define ERROR_CODE_EXIT -1 111 | #define ERROR_CODE_EXCHANGE -2 // 与内核交换信息失败 112 | #define ERROR_CODE_WRONG_IP -11 // 错误的IP格式 113 | #define ERROR_CODE_NO_SUCH_RULE -12 114 | 115 | /** 116 | * @brief 内核回应包 117 | */ 118 | struct KernelResponse { 119 | int code; // <0 代表请求失败,失败码; >=0 代表body长度 120 | void *data; // 回应包指针,记得free 121 | struct KernelResponseHeader *header; // 不要free;指向data中的头部 122 | void *body; // 不要free;指向data中的Body 123 | }; 124 | 125 | /** 126 | * @brief 与内核交换数据 127 | * @param smsg: 发送的消息 128 | * @param slen: 发送消息的长度 129 | * @return KernelResponse: 接收到的回应,其中data字段记得free 130 | */ 131 | struct KernelResponse exchangeMsgK(void *smsg, unsigned int slen); 132 | 133 | // ----- 与内核交互函数 ----- 134 | 135 | struct KernelResponse addFilterRule(char *after,char *name,char *sip,char *dip,unsigned int sport,unsigned int dport,u_int8_t proto,unsigned int log,unsigned int action); // 新增一条过滤规则,其中,sport/dport为端口范围:高2字节为最小 低2字节为最大 136 | struct KernelResponse changeFilterRule(int key,char *name,char *sip,char *dip,unsigned int sport,unsigned int dport,u_int8_t proto,unsigned int log,unsigned int action); // 修改一条过滤规则,其中,sport/dport为端口范围:高2字节为最小 低2字节为最大 137 | struct KernelResponse delFilterRule(char *name); 138 | struct KernelResponse getAllFilterRules(void); 139 | struct KernelResponse addNATRule(char *sip,char *natIP,unsigned short minport,unsigned short maxport); 140 | struct KernelResponse delNATRule(int num); 141 | struct KernelResponse getAllNATRules(void); 142 | struct KernelResponse setDefaultAction(unsigned int action); 143 | struct KernelResponse getLogs(unsigned int num); // num=0时,获取所有日志 144 | struct KernelResponse getAllConns(void); 145 | 146 | // ----- 一些工具函数 ------ 147 | 148 | int IPstr2IPint(const char *ipStr, unsigned int *ip, unsigned int *mask); 149 | int IPint2IPstr(unsigned int ip, unsigned int mask, char *ipStr); 150 | int IPint2IPstrNoMask(unsigned int ip, char *ipStr); 151 | int IPint2IPstrWithPort(unsigned int ip, unsigned short port, char *ipStr); 152 | 153 | #endif -------------------------------------------------------------------------------- /src/common/tools.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int IPstr2IPint(const char *ipStr, unsigned int *ip, unsigned int *mask){ 4 | // init 5 | int p = -1, count = 0; 6 | unsigned int len = 0, tmp = 0, r_mask = 0, r_ip = 0,i; 7 | for(i = 0; i < strlen(ipStr); i++){ 8 | if(!(ipStr[i]>='0' && ipStr[i]<='9') && ipStr[i]!='.' && ipStr[i]!='/') { 9 | return -1; 10 | } 11 | } 12 | // 获取掩码 13 | for(i = 0; i < strlen(ipStr); i++){ 14 | if(p != -1){ 15 | len *= 10; 16 | len += ipStr[i] - '0'; 17 | } 18 | else if(ipStr[i] == '/') 19 | p = i; 20 | } 21 | if(len > 32 || (p>=0 && p<7)) { 22 | return -1; 23 | } 24 | if(p != -1){ 25 | if(len) 26 | r_mask = 0xFFFFFFFF << (32 - len); 27 | } 28 | else r_mask = 0xFFFFFFFF; 29 | // 获取IP 30 | for(i = 0; i < (p>=0 ? p : strlen(ipStr)); i++){ 31 | if(ipStr[i] == '.'){ 32 | r_ip = r_ip | (tmp << (8 * (3 - count))); 33 | tmp = 0; 34 | count++; 35 | continue; 36 | } 37 | tmp *= 10; 38 | tmp += ipStr[i] - '0'; 39 | if(tmp>256 || count>3) 40 | return -2; 41 | } 42 | r_ip = r_ip | tmp; 43 | *ip = r_ip; 44 | *mask = r_mask; 45 | return 0; 46 | } 47 | 48 | int IPint2IPstr(unsigned int ip, unsigned int mask, char *ipStr) { 49 | unsigned int i,ips[4],maskNum = 32; 50 | if(ipStr == NULL) { 51 | return -1; 52 | } 53 | if(mask == 0) 54 | maskNum = 0; 55 | else { 56 | while((mask & 1u) == 0) { 57 | maskNum--; 58 | mask >>= 1; 59 | } 60 | } 61 | for(i=0;i<4;i++) { 62 | ips[i] = ((ip >> ((3-i)*8)) & 0xFFU); 63 | } 64 | sprintf(ipStr, "%u.%u.%u.%u/%u", ips[0], ips[1], ips[2], ips[3], maskNum); 65 | return 0; 66 | } 67 | 68 | int IPint2IPstrNoMask(unsigned int ip, char *ipStr) { 69 | unsigned int i,ips[4]; 70 | if(ipStr == NULL) { 71 | return -1; 72 | } 73 | for(i=0;i<4;i++) { 74 | ips[i] = ((ip >> ((3-i)*8)) & 0xFFU); 75 | } 76 | sprintf(ipStr, "%u.%u.%u.%u", ips[0], ips[1], ips[2], ips[3]); 77 | return 0; 78 | } 79 | 80 | int IPint2IPstrWithPort(unsigned int ip, unsigned short port, char *ipStr) { 81 | if(port == 0) { 82 | return IPint2IPstrNoMask(ip, ipStr); 83 | } 84 | unsigned int i,ips[4]; 85 | if(ipStr == NULL) { 86 | return -1; 87 | } 88 | for(i=0;i<4;i++) { 89 | ips[i] = ((ip >> ((3-i)*8)) & 0xFFU); 90 | } 91 | sprintf(ipStr, "%u.%u.%u.%u:%u", ips[0], ips[1], ips[2], ips[3], port); 92 | return 0; 93 | } -------------------------------------------------------------------------------- /src/kernel_mod/Makefile: -------------------------------------------------------------------------------- 1 | MODULE_NAME = fwko 2 | 3 | SRC := tools.c helpers/netlink_helper.c helpers/log_helper.c helpers/rule_helper.c helpers/conn_helper.c helpers/nat_helper.c helpers/app_helper.c hooks/hook_main.c hooks/hook_nat.c mod_main.c 4 | 5 | KDIR := /lib/modules/$(shell uname -r)/build 6 | 7 | EXTRA_CFLAGS := -I$(src)/include -I$(src)/hooks -I$(src)/helpers 8 | 9 | $(MODULE_NAME)-objs = $(SRC:.c=.o) 10 | obj-m := $(MODULE_NAME).o 11 | PWD := $(shell pwd) 12 | 13 | all: 14 | $(MAKE) -C $(KDIR) M=$(PWD) modules 15 | 16 | install: 17 | insmod $(PWD)/$(MODULE_NAME).ko 18 | $(MAKE) clean 19 | 20 | clean: 21 | $(MAKE) -C $(KDIR) M=$(PWD) modules clean 22 | rm -rf modules.order 23 | rm -f *.symvers 24 | -------------------------------------------------------------------------------- /src/kernel_mod/helpers/app_helper.c: -------------------------------------------------------------------------------- 1 | #include "helper.h" 2 | 3 | extern unsigned int DEFAULT_ACTION; 4 | 5 | int sendMsgToApp(unsigned int pid, const char *msg) { 6 | void* mem; 7 | unsigned int rspLen; 8 | struct KernelResponseHeader *rspH; 9 | rspLen = sizeof(struct KernelResponseHeader) + strlen(msg) + 1; 10 | mem = kzalloc(rspLen, GFP_ATOMIC); 11 | if(mem == NULL) { 12 | printk(KERN_WARNING "[fw k2app] sendMsgToApp kzalloc fail.\n"); 13 | return 0; 14 | } 15 | rspH = (struct KernelResponseHeader *)mem; 16 | rspH->bodyTp = RSP_MSG; 17 | rspH->arrayLen = strlen(msg); 18 | memcpy(mem+sizeof(struct KernelResponseHeader), msg, strlen(msg)); 19 | nlSend(pid, mem, rspLen); 20 | kfree(mem); 21 | return rspLen; 22 | } 23 | 24 | void dealWithSetAction(unsigned int action) { 25 | if(action != NF_ACCEPT) { 26 | struct IPRule rule = { 27 | .smask = 0, 28 | .dmask = 0, 29 | .sport = -1, 30 | .dport = -1 31 | }; // 清除全部连接 32 | eraseConnRelated(rule); 33 | } 34 | } 35 | 36 | int dealAppMessage(unsigned int pid, void *msg, unsigned int len) { 37 | struct APPRequest *req; 38 | struct KernelResponseHeader *rspH; 39 | void* mem; 40 | unsigned int rspLen = 0; 41 | req = (struct APPRequest *) msg; 42 | switch (req->tp) 43 | { 44 | case REQ_GETAllIPLogs: 45 | mem = formAllIPLogs(req->msg.num, &rspLen); 46 | if(mem == NULL) { 47 | printk(KERN_WARNING "[fw k2app] formAllIPLogs fail.\n"); 48 | sendMsgToApp(pid, "form all logs fail."); 49 | break; 50 | } 51 | nlSend(pid, mem, rspLen); 52 | kfree(mem); 53 | break; 54 | case REQ_GETAllConns: 55 | mem = formAllConns(&rspLen); 56 | if(mem == NULL) { 57 | printk(KERN_WARNING "[fw k2app] formAllConns fail.\n"); 58 | sendMsgToApp(pid, "form all conns fail."); 59 | break; 60 | } 61 | nlSend(pid, mem, rspLen); 62 | kfree(mem); 63 | break; 64 | case REQ_GETAllIPRules: 65 | mem = formAllIPRules(&rspLen); 66 | if(mem == NULL) { 67 | printk(KERN_WARNING "[fw k2app] formAllIPRules fail.\n"); 68 | sendMsgToApp(pid, "form all rules fail."); 69 | break; 70 | } 71 | nlSend(pid, mem, rspLen); 72 | kfree(mem); 73 | break; 74 | case REQ_ADDIPRule: 75 | if(addIPRuleToChain(req->ruleName, req->msg.ipRule)==NULL) { 76 | rspLen = sendMsgToApp(pid, "Fail: no such rule or retry it."); 77 | printk("[fw k2app] add rule fail.\n"); 78 | } else { 79 | rspLen = sendMsgToApp(pid, "Success."); 80 | printk("[fw k2app] add one rule success: %s.\n", req->msg.ipRule.name); 81 | } 82 | break; 83 | case REQ_CHANGEIPRule: 84 | if(changeIPRuleOfChain(req->num, req->msg.ipRule)==NULL) { 85 | rspLen = sendMsgToApp(pid, "Fail: no such rule or retry it."); 86 | printk("[fw k2app] add rule fail.\n"); 87 | } else { 88 | rspLen = sendMsgToApp(pid, "Success."); 89 | printk("[fw k2app] add one rule success: %s.\n", req->msg.ipRule.name); 90 | } 91 | break; 92 | case REQ_DELIPRule: 93 | rspLen = sizeof(struct KernelResponseHeader); 94 | rspH = (struct KernelResponseHeader *)kzalloc(rspLen, GFP_KERNEL); 95 | if(rspH == NULL) { 96 | printk(KERN_WARNING "[fw k2app] kzalloc fail.\n"); 97 | sendMsgToApp(pid, "form rsp fail but del maybe success."); 98 | break; 99 | } 100 | rspH->bodyTp = RSP_Only_Head; 101 | rspH->arrayLen = delIPRuleFromChain(req->ruleName); 102 | printk("[fw k2app] success del %d rules.\n", rspH->arrayLen); 103 | nlSend(pid, rspH, rspLen); 104 | kfree(rspH); 105 | break; 106 | case REQ_GETNATRules: 107 | mem = formAllNATRules(&rspLen); 108 | if(mem == NULL) { 109 | printk(KERN_WARNING "[fw k2app] formAllNATRules fail.\n"); 110 | sendMsgToApp(pid, "form all NAT rules fail."); 111 | break; 112 | } 113 | nlSend(pid, mem, rspLen); 114 | kfree(mem); 115 | break; 116 | case REQ_ADDNATRule: 117 | if(addNATRuleToChain(req->msg.natRule)==NULL) { 118 | rspLen = sendMsgToApp(pid, "Fail: please retry it."); 119 | printk("[fw k2app] add NAT rule fail.\n"); 120 | } else { 121 | rspLen = sendMsgToApp(pid, "Success."); 122 | printk("[fw k2app] add one NAT rule success.\n"); 123 | } 124 | break; 125 | case REQ_DELNATRule: 126 | rspLen = sizeof(struct KernelResponseHeader); 127 | rspH = (struct KernelResponseHeader *)kzalloc(rspLen, GFP_KERNEL); 128 | if(rspH == NULL) { 129 | printk(KERN_WARNING "[fw k2app] kzalloc fail.\n"); 130 | sendMsgToApp(pid, "form rsp fail but del maybe success."); 131 | break; 132 | } 133 | rspH->bodyTp = RSP_Only_Head; 134 | rspH->arrayLen = delNATRuleFromChain(req->msg.num); 135 | printk("[fw k2app] success del %d NAT rules.\n", rspH->arrayLen); 136 | nlSend(pid, rspH, rspLen); 137 | kfree(rspH); 138 | break; 139 | case REQ_SETAction: 140 | if(req->msg.defaultAction == NF_ACCEPT) { 141 | DEFAULT_ACTION = NF_ACCEPT; 142 | rspLen = sendMsgToApp(pid, "Set default action to ACCEPT."); 143 | printk("[fw k2app] Set default action to NF_ACCEPT.\n"); 144 | } else { 145 | DEFAULT_ACTION = NF_DROP; 146 | rspLen = sendMsgToApp(pid, "Set default action to DROP."); 147 | printk("[fw k2app] Set default action to NF_DROP.\n"); 148 | } 149 | dealWithSetAction(DEFAULT_ACTION); 150 | break; 151 | default: 152 | rspLen = sendMsgToApp(pid, "No such req."); 153 | break; 154 | } 155 | return rspLen; 156 | } -------------------------------------------------------------------------------- /src/kernel_mod/helpers/conn_helper.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | #include "helper.h" 3 | 4 | // --- 红黑树相关:查找、插入、删除 --- 5 | 6 | static struct rb_root connRoot = RB_ROOT; 7 | static DEFINE_RWLOCK(connLock); 8 | 9 | // 比较连接标识符 10 | int connKeyCmp(conn_key_t l, conn_key_t r) { 11 | register int i; 12 | for(i=0;irb_node; 26 | while (node) { 27 | struct connNode *data = rb_entry(node, struct connNode, node); 28 | result = connKeyCmp(key, data->key); 29 | if (result < 0) 30 | node = node->rb_left; 31 | else if (result > 0) 32 | node = node->rb_right; 33 | else { // 找到节点 34 | read_unlock(&connLock); 35 | return data; 36 | } 37 | } 38 | read_unlock(&connLock); 39 | return NULL; 40 | } 41 | 42 | // 插入新节点,返回节点 43 | struct connNode *insertNode(struct rb_root *root, struct connNode *data) { 44 | struct rb_node **new, *parent; 45 | if(data == NULL) { 46 | return NULL; 47 | } 48 | parent = NULL; 49 | read_lock(&connLock); 50 | new = &(root->rb_node); 51 | /* Figure out where to put new node */ 52 | while (*new) { 53 | struct connNode *this = rb_entry(*new, struct connNode, node); 54 | int result = connKeyCmp(data->key, this->key); 55 | parent = *new; 56 | if (result < 0) 57 | new = &((*new)->rb_left); 58 | else if (result > 0) 59 | new = &((*new)->rb_right); 60 | else { // 已存在 61 | read_unlock(&connLock); 62 | return this; 63 | } 64 | } 65 | /* Add new node and rebalance tree. */ 66 | read_unlock(&connLock); 67 | write_lock(&connLock); 68 | rb_link_node(&data->node, parent, new); 69 | rb_insert_color(&data->node, root); 70 | write_unlock(&connLock); 71 | return data; // 插入成功 72 | } 73 | 74 | // 删除节点 75 | void eraseNode(struct rb_root *root, struct connNode *node) { 76 | if(node != NULL) { 77 | write_lock(&connLock); 78 | rb_erase(&(node->node), root); 79 | write_unlock(&connLock); 80 | kfree(node); 81 | } 82 | } 83 | 84 | // --- 业务相关 --- 85 | // 是否超时 86 | int isTimeout(unsigned long expires) { 87 | return (jiffies >= expires)? 1 : 0; // 当前时间 >= 超时时间 ? 88 | } 89 | 90 | void addConnExpires(struct connNode *node, unsigned int plus) { 91 | if(node == NULL) 92 | return ; 93 | write_lock(&connLock); 94 | node->expires = timeFromNow(plus); 95 | write_unlock(&connLock); 96 | } 97 | 98 | // 检查是否存在指定连接 99 | struct connNode *hasConn(unsigned int sip, unsigned int dip, unsigned short sport, unsigned short dport) { 100 | conn_key_t key; 101 | struct connNode *node = NULL; 102 | // 构建标识符 103 | key[0] = sip; 104 | key[1] = dip; 105 | key[2] = ((((unsigned int)sport) << 16) | ((unsigned int)dport)); 106 | // 查找节点 107 | node = searchNode(&connRoot, key); 108 | addConnExpires(node, CONN_EXPIRES); // 重新设置超时时间 109 | return node; 110 | } 111 | 112 | // 新建连接 113 | struct connNode *addConn(unsigned int sip, unsigned int dip, unsigned short sport, unsigned short dport, u_int8_t proto, u_int8_t log) { 114 | // 初始化 115 | struct connNode *node = (struct connNode *)kzalloc(sizeof(connNode), GFP_ATOMIC); 116 | if(node == NULL) { 117 | printk(KERN_WARNING "[fw conns] kzalloc fail.\n"); 118 | return 0; 119 | } 120 | node->needLog = log; 121 | node->protocol = proto; 122 | node->expires = timeFromNow(CONN_EXPIRES); // 设置超时时间 123 | node->natType = NAT_TYPE_NO; 124 | // 构建标识符 125 | node->key[0] = sip; 126 | node->key[1] = dip; 127 | node->key[2] = ((((unsigned int)sport) << 16) | ((unsigned int)dport)); 128 | // 插入节点 129 | return insertNode(&connRoot, node); 130 | } 131 | 132 | // 设置连接的NAT 133 | int setConnNAT(struct connNode *node, struct NATRecord record, int natType) { 134 | if(node==NULL) 135 | return 0; 136 | write_lock(&connLock); 137 | node->natType = natType; 138 | node->nat = record; 139 | write_unlock(&connLock); 140 | return 1; 141 | } 142 | 143 | // 获取新的可用NAT端口 144 | unsigned short getNewNATPort(struct NATRecord rule) { 145 | struct rb_node *node; 146 | struct connNode *now; 147 | unsigned short port, inUse; 148 | // 遍历 149 | if(rule.nowPort > rule.dport || rule.nowPort < rule.sport) 150 | rule.nowPort = rule.dport; 151 | for(port = rule.nowPort + 1; port != rule.nowPort; port++) { 152 | if(port > rule.dport || port < rule.sport) 153 | port = rule.sport; 154 | read_lock(&connLock); 155 | for(node = rb_first(&connRoot), inUse = 0; node; node=rb_next(node)) { 156 | now = rb_entry(node, struct connNode, node); 157 | if(now->natType != NAT_TYPE_SRC) 158 | continue; 159 | if(now->nat.daddr != rule.daddr) 160 | continue; 161 | if(port == now->nat.dport) { 162 | inUse = 1; 163 | break; 164 | } 165 | } 166 | read_unlock(&connLock); 167 | if(!inUse) { 168 | return port; 169 | } 170 | } 171 | return 0; 172 | } 173 | 174 | // 将所有已有连接形成Netlink回包 175 | void* formAllConns(unsigned int *len) { 176 | struct KernelResponseHeader *head; 177 | struct rb_node *node; 178 | struct connNode *now; 179 | struct ConnLog log; 180 | void *mem,*p; 181 | unsigned int count; 182 | read_lock(&connLock); 183 | // 计算总量 184 | for (node=rb_first(&connRoot),count=0;node;node=rb_next(node),count++); 185 | // 申请回包空间 186 | *len = sizeof(struct KernelResponseHeader) + sizeof(struct ConnLog) * count; 187 | mem = kzalloc(*len, GFP_ATOMIC); 188 | if(mem == NULL) { 189 | printk(KERN_WARNING "[fw conns] formAllConns kzalloc fail.\n"); 190 | read_unlock(&connLock); 191 | return NULL; 192 | } 193 | // 构建回包 194 | head = (struct KernelResponseHeader *)mem; 195 | head->bodyTp = RSP_ConnLogs; 196 | head->arrayLen = count; 197 | p=(mem + sizeof(struct KernelResponseHeader)); 198 | for (node = rb_first(&connRoot); node; node=rb_next(node),p=p+sizeof(struct ConnLog)) { 199 | now = rb_entry(node, struct connNode, node); 200 | log.saddr = now->key[0]; 201 | log.daddr = now->key[1]; 202 | log.sport = (unsigned short)(now->key[2] >> 16); 203 | log.dport = (unsigned short)(now->key[2] & 0xFFFFu); 204 | log.protocol = now->protocol; 205 | log.natType = now->natType; 206 | log.nat = now->nat; 207 | memcpy(p, &log, sizeof(struct ConnLog)); 208 | } 209 | read_unlock(&connLock); 210 | return mem; 211 | } 212 | 213 | // 依据过滤规则,删除相关连接 214 | int eraseConnRelated(struct IPRule rule) { 215 | struct rb_node *node; 216 | unsigned short sport,dport; 217 | struct connNode *needDel = NULL; 218 | unsigned int count = 0; 219 | int hasChange = 1; // 连接池是否有更改(删除节点) 220 | // 初始化 221 | rule.protocol = IPPROTO_IP; 222 | // 删除相关节点 223 | while(hasChange) { // 有更改时,持续遍历,防止漏下节点 224 | hasChange = 0; 225 | read_lock(&connLock); 226 | for (node = rb_first(&connRoot); node; node = rb_next(node)) { 227 | needDel = rb_entry(node, struct connNode, node); 228 | sport = (unsigned short)(needDel->key[2] >> 16); 229 | dport = (unsigned short)(needDel->key[2] & 0xFFFFu); 230 | if(matchOneRule(&rule, needDel->key[0], needDel->key[1], sport, dport, needDel->protocol)) { // 相关规则 231 | hasChange = 1; 232 | break; 233 | } 234 | } 235 | read_unlock(&connLock); 236 | if(hasChange) { // 需要删除 开始删除节点 237 | eraseNode(&connRoot, needDel); 238 | count++; 239 | } 240 | } 241 | printk("[fw conns] erase all related conn finish.\n"); 242 | return count; 243 | } 244 | 245 | // 刷新连接池(定时器所用),删除超时连接 246 | int rollConn(void) { 247 | struct rb_node *node; 248 | struct connNode *needDel = NULL; 249 | int hasChange = 1; // 连接池是否有更改(删除节点) 250 | //printk("[fw conns] flush all conn start.\n"); 251 | while(hasChange) { // 有更改时,持续遍历,防止漏下节点 252 | hasChange = 0; 253 | read_lock(&connLock); 254 | for (node = rb_first(&connRoot); node; node = rb_next(node)) { 255 | needDel = rb_entry(node, struct connNode, node); 256 | if(isTimeout(needDel->expires)) { // 超时 确认删除 257 | hasChange = 1; 258 | break; 259 | } 260 | } 261 | read_unlock(&connLock); 262 | if(hasChange) { // 需要删除 开始删除节点 263 | eraseNode(&connRoot, needDel); 264 | } 265 | } 266 | //printk("[fw conns] flush all conn finish.\n"); 267 | return 0; 268 | } 269 | 270 | // --- 定时器相关 --- 271 | static struct timer_list conn_timer;//定义计时器 272 | 273 | // 计时器回调函数 274 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) 275 | void conn_timer_callback(unsigned long arg) { 276 | #else 277 | void conn_timer_callback(struct timer_list *t) { 278 | #endif 279 | rollConn(); 280 | mod_timer(&conn_timer, timeFromNow(CONN_ROLL_INTERVAL)); //重新激活定时器 281 | } 282 | 283 | // 初始化连接池相关内容(包括定时器) 284 | void conn_init(void) { 285 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) 286 | init_timer(&conn_timer); 287 | conn_timer.function = &conn_timer_callback;//设置定时器回调方法 288 | conn_timer.data = ((unsigned long)0); 289 | #else 290 | timer_setup(&conn_timer, conn_timer_callback, 0); 291 | #endif 292 | conn_timer.expires = timeFromNow(CONN_ROLL_INTERVAL);//超时时间设置为CONN_ROLL_INTERVAL秒后 293 | add_timer(&conn_timer);//激活定时器 294 | } 295 | 296 | // 关闭连接池 297 | void conn_exit(void) { 298 | del_timer(&conn_timer); 299 | } 300 | -------------------------------------------------------------------------------- /src/kernel_mod/helpers/log_helper.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | #include "helper.h" 3 | 4 | static struct IPLog *logHead = NULL,*logTail = NULL; 5 | static unsigned int logNum = 0; 6 | static DEFINE_RWLOCK(logLock); 7 | 8 | // 释放首部多余的日志节点 整理链表 9 | int rollLog(void) { 10 | struct IPLog *tmp; 11 | unsigned int count = 0; 12 | printk("[fw logs] roll log chain.\n"); 13 | write_lock(&logLock); 14 | while(logNum > MAX_LOG_LEN) { 15 | if(logHead == NULL) { // 链表头指针丢失 16 | logHead = logTail; 17 | logNum = logTail==NULL ? 0 : 1; 18 | write_unlock(&logLock); 19 | return count; 20 | } 21 | tmp = logHead; 22 | logHead = logHead->nx; 23 | logNum--; 24 | count++; 25 | if(logTail == tmp) { // 链表尾指针丢失 26 | logTail = logHead; 27 | logNum = logTail==NULL ? 0 : 1; 28 | } 29 | kfree(tmp); 30 | } 31 | write_unlock(&logLock); 32 | return count; 33 | } 34 | 35 | // 新增日志记录 36 | int addLog(struct IPLog log) { 37 | struct IPLog *newLog; 38 | newLog = (struct IPLog *) kzalloc(sizeof(struct IPLog), GFP_KERNEL); 39 | if(newLog == NULL) { 40 | printk(KERN_WARNING "[fw logs] kzalloc fail.\n"); 41 | return 0; 42 | } 43 | memcpy(newLog, &log, sizeof(struct IPLog)); 44 | newLog->nx = NULL; 45 | // 新增日志至日志链表 46 | write_lock(&logLock); 47 | if(logTail == NULL) { // 日志链表为空 48 | logTail = newLog; 49 | logHead = logTail; 50 | logNum = 1; 51 | write_unlock(&logLock); 52 | //printk("[fw logs] add a log at head.\n"); 53 | return 1; 54 | } 55 | logTail->nx = newLog; 56 | logTail = newLog; 57 | logNum++; 58 | write_unlock(&logLock); 59 | //printk("[fw logs] add a log.\n"); 60 | if(logNum > MAX_LOG_LEN) { 61 | rollLog(); 62 | } 63 | return 1; 64 | } 65 | 66 | int addLogBySKB(unsigned int action, struct sk_buff *skb) { 67 | struct IPLog log; 68 | unsigned short sport,dport; 69 | struct iphdr *header; 70 | struct timeval now = { 71 | .tv_sec = 0, 72 | .tv_usec = 0 73 | }; 74 | do_gettimeofday(&now); 75 | log.tm = now.tv_sec; 76 | header = ip_hdr(skb); 77 | getPort(skb,header,&sport,&dport); 78 | log.saddr = ntohl(header->saddr); 79 | log.daddr = ntohl(header->daddr); 80 | log.sport = sport; 81 | log.dport = dport; 82 | log.len = ntohs(header->tot_len) - (header->ihl * 4); 83 | log.protocol = header->protocol; 84 | log.action = action; 85 | log.nx = NULL; 86 | return addLog(log); 87 | } 88 | 89 | // 将所有过滤日志形成Netlink回包 90 | void* formAllIPLogs(unsigned int num, unsigned int *len) { 91 | struct KernelResponseHeader *head; 92 | struct IPLog *now; 93 | void *mem,*p; 94 | unsigned int count; 95 | read_lock(&logLock); 96 | for(now=logHead,count=0;now!=NULL;now=now->nx,count++); // 计算日志总量 97 | printk("[fw logs] form logs count=%d, need num=%d.\n", count, num); 98 | if(num == 0 || num > count) 99 | num = count; 100 | *len = sizeof(struct KernelResponseHeader) + sizeof(struct IPLog) * num; // 申请回包空间 101 | mem = kzalloc(*len, GFP_ATOMIC); 102 | if(mem == NULL) { 103 | printk(KERN_WARNING "[fw logs] formAllIPLogs kzalloc fail.\n"); 104 | read_unlock(&logLock); 105 | return NULL; 106 | } 107 | // 构建回包 108 | head = (struct KernelResponseHeader *)mem; 109 | head->bodyTp = RSP_IPLogs; 110 | head->arrayLen = num; 111 | p=(mem + sizeof(struct KernelResponseHeader)); 112 | for(now=logHead;now!=NULL;now=now->nx) { 113 | if(count > num) { // 只取最后num个日志 114 | count--; 115 | continue; 116 | } 117 | memcpy(p, now, sizeof(struct IPLog)); 118 | p=p+sizeof(struct IPLog); 119 | } 120 | read_unlock(&logLock); 121 | return mem; 122 | } 123 | -------------------------------------------------------------------------------- /src/kernel_mod/helpers/nat_helper.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | #include "helper.h" 3 | 4 | static struct NATRecord *natRuleHead = NULL; 5 | static DEFINE_RWLOCK(natRuleLock); 6 | 7 | // 首部新增一条NAT规则 8 | struct NATRecord * addNATRuleToChain(struct NATRecord rule) { 9 | struct NATRecord *newRule; 10 | newRule = (struct NATRecord *) kzalloc(sizeof(struct NATRecord), GFP_KERNEL); 11 | if(newRule == NULL) { 12 | printk(KERN_WARNING "[fw nat] kzalloc fail.\n"); 13 | return NULL; 14 | } 15 | memcpy(newRule, &rule, sizeof(struct NATRecord)); 16 | // 新增规则至规则链表 17 | write_lock(&natRuleLock); 18 | if(natRuleHead == NULL) { 19 | natRuleHead = newRule; 20 | natRuleHead->nx = NULL; 21 | write_unlock(&natRuleLock); 22 | return newRule; 23 | } 24 | newRule->nx = natRuleHead; 25 | natRuleHead = newRule; 26 | write_unlock(&natRuleLock); 27 | return newRule; 28 | } 29 | 30 | // 删除序号为num的NAT规则 31 | int delNATRuleFromChain(int num) { 32 | struct NATRecord *now,*tmp; 33 | struct IPRule iprule; 34 | int count = 0, flag=0; 35 | if(natRuleHead==NULL) 36 | return 0; 37 | write_lock(&natRuleLock); 38 | if(num == 0){ 39 | tmp = natRuleHead; 40 | natRuleHead = tmp->nx; 41 | flag = 1; // 找到删除结点 42 | } 43 | for(now=natRuleHead, count=1;!flag && now!=NULL && now->nx!=NULL;now=now->nx,count++) { 44 | if(count == num) { // 删除规则 45 | tmp = now->nx; 46 | now->nx = now->nx->nx; 47 | flag = 1; 48 | } 49 | } 50 | if(flag){ 51 | iprule.saddr = tmp->saddr; // 消除连接池影响 52 | iprule.smask = tmp->smask; 53 | iprule.dmask = 0; 54 | iprule.sport = 0xFFFFu; 55 | iprule.dport = 0xFFFFu; 56 | eraseConnRelated(iprule); 57 | kfree(tmp); 58 | write_unlock(&natRuleLock); 59 | return 1; 60 | } 61 | write_unlock(&natRuleLock); 62 | return 0; 63 | } 64 | 65 | // 将所有NAT规则形成Netlink回包 66 | void* formAllNATRules(unsigned int *len) { 67 | struct KernelResponseHeader *head; 68 | struct NATRecord *now; 69 | void *mem,*p; 70 | unsigned int count; 71 | read_lock(&natRuleLock); 72 | for(now=natRuleHead,count=0;now!=NULL;now=now->nx,count++); 73 | *len = sizeof(struct KernelResponseHeader) + sizeof(struct NATRecord)*count; 74 | mem = kzalloc(*len, GFP_ATOMIC); 75 | if(mem == NULL) { 76 | printk(KERN_WARNING "[fw nat] kzalloc fail.\n"); 77 | read_unlock(&natRuleLock); 78 | return NULL; 79 | } 80 | head = (struct KernelResponseHeader *)mem; 81 | head->bodyTp = RSP_NATRules; 82 | head->arrayLen = count; 83 | for(now=natRuleHead,p=(mem + sizeof(struct KernelResponseHeader));now!=NULL;now=now->nx,p=p+sizeof(struct NATRecord)) 84 | memcpy(p, now, sizeof(struct NATRecord)); 85 | read_unlock(&natRuleLock); 86 | return mem; 87 | } 88 | 89 | struct NATRecord *matchNATRule(unsigned int sip, unsigned int dip, int *isMatch) { 90 | struct NATRecord *now; 91 | *isMatch = 0; 92 | read_lock(&natRuleLock); 93 | for(now=natRuleHead;now!=NULL;now=now->nx) { 94 | if(isIPMatch(sip, now->saddr, now->smask) && 95 | !isIPMatch(dip, now->saddr, now->smask) && 96 | dip != now->daddr) { 97 | read_unlock(&natRuleLock); 98 | *isMatch = 1; 99 | return now; 100 | } 101 | } 102 | read_unlock(&natRuleLock); 103 | return NULL; 104 | } 105 | 106 | struct NATRecord genNATRecord(unsigned int preIP, unsigned int afterIP, unsigned short prePort, unsigned short afterPort) { 107 | struct NATRecord record; 108 | record.saddr = preIP; 109 | record.sport = prePort; 110 | record.daddr = afterIP; 111 | record.dport = afterPort; 112 | return record; 113 | } 114 | -------------------------------------------------------------------------------- /src/kernel_mod/helpers/netlink_helper.c: -------------------------------------------------------------------------------- 1 | #include "helper.h" 2 | 3 | static struct sock *nlsk = NULL; 4 | 5 | int nlSend(unsigned int pid, void *data, unsigned int len) { 6 | int retval; 7 | struct nlmsghdr *nlh; 8 | struct sk_buff *skb; 9 | // init sk_buff 10 | skb = nlmsg_new(len, GFP_ATOMIC); 11 | if (skb == NULL) { 12 | printk(KERN_WARNING "[fw netlink] alloc reply nlmsg skb failed!\n"); 13 | return -1; 14 | } 15 | nlh = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(len) - NLMSG_HDRLEN, 0); 16 | // send data 17 | memcpy(NLMSG_DATA(nlh), data, len); 18 | //NETLINK_CB(skb).portid = 0; 19 | NETLINK_CB(skb).dst_group = 0; 20 | retval = netlink_unicast(nlsk, skb, pid, MSG_DONTWAIT); 21 | printk("[fw netlink] send to user pid=%d,len=%d,ret=%d\n", pid, nlh->nlmsg_len - NLMSG_SPACE(0), retval); 22 | return retval; 23 | } 24 | 25 | void nlRecv(struct sk_buff *skb) { 26 | void *data; 27 | struct nlmsghdr *nlh = NULL; 28 | unsigned int pid,len; 29 | // check skb 30 | nlh = nlmsg_hdr(skb); 31 | if ((nlh->nlmsg_len < NLMSG_HDRLEN) || (skb->len < nlh->nlmsg_len)) { 32 | printk(KERN_WARNING "[fw netlink] Illegal netlink packet!\n"); 33 | return; 34 | } 35 | // deal data 36 | data = NLMSG_DATA(nlh); 37 | pid = nlh->nlmsg_pid; 38 | len = nlh->nlmsg_len - NLMSG_SPACE(0); 39 | if(lennx = NULL; 23 | write_unlock(&ipRuleLock); 24 | return newRule; 25 | } 26 | if(strlen(after)==0) { 27 | newRule->nx = ipRuleHead; 28 | ipRuleHead = newRule; 29 | write_unlock(&ipRuleLock); 30 | return newRule; 31 | } 32 | for(now=ipRuleHead;now!=NULL;now=now->nx) { 33 | if(strcmp(now->name, after)==0) { 34 | newRule->nx = now->nx; 35 | now->nx = newRule; 36 | write_unlock(&ipRuleLock); 37 | return newRule; 38 | } 39 | } 40 | // 添加失败 41 | write_unlock(&ipRuleLock); 42 | kfree(newRule); 43 | return NULL; 44 | } 45 | 46 | // 修改第key条规则(序号从1开始) 47 | struct IPRule * changeIPRuleOfChain(int nowI, struct IPRule rule) { 48 | int i = 1; //nowI==1 49 | struct IPRule *now; 50 | // 新增规则至规则链表 51 | write_lock(&ipRuleLock); 52 | if(rule.action != NF_ACCEPT) 53 | eraseConnRelated(rule); // 消除新增规则的影响 54 | for(now = ipRuleHead;now !=NULL && i < nowI;now=now->nx, i++) ; 55 | if(now != NULL) { 56 | if(rule.saddr!=0) { 57 | now->saddr = rule.saddr; 58 | now->smask = rule.smask; 59 | } 60 | if(rule.daddr!=0) { 61 | now->daddr = rule.daddr; 62 | now->dmask = rule.dmask; 63 | } 64 | if(rule.sport != 0 || rule.dport != 0){ 65 | now->sport = rule.sport; 66 | now->dport = rule.dport; 67 | } 68 | now->log = (rule.log!=2)? rule.log: now->log; 69 | now->action = (rule.action!=2)? rule.action: now->action; 70 | now->protocol = (rule.protocol!=255)? rule.protocol: now->protocol; 71 | if(strcmp(rule.name, "-1")!=0) 72 | strncpy(now->name, rule.name, MAXRuleNameLen); 73 | write_unlock(&ipRuleLock); 74 | return now; 75 | } 76 | // 添加失败 77 | write_unlock(&ipRuleLock); 78 | return NULL; 79 | } 80 | 81 | // 删除所有名称为name的规则 82 | int delIPRuleFromChain(char name[]) { 83 | struct IPRule *now,*tmp; 84 | int count = 0; 85 | write_lock(&ipRuleLock); 86 | while(ipRuleHead!=NULL && strcmp(ipRuleHead->name,name)==0) { 87 | tmp = ipRuleHead; 88 | ipRuleHead = ipRuleHead->nx; 89 | eraseConnRelated(*tmp); // 消除删除规则的影响 90 | kfree(tmp); 91 | count++; 92 | } 93 | for(now=ipRuleHead;now!=NULL && now->nx!=NULL;) { 94 | if(strcmp(now->nx->name,name)==0) { // 删除下条规则 95 | tmp = now->nx; 96 | now->nx = now->nx->nx; 97 | eraseConnRelated(*tmp); // 消除删除规则的影响 98 | kfree(tmp); 99 | count++; 100 | } else { 101 | now = now->nx; 102 | } 103 | } 104 | write_unlock(&ipRuleLock); 105 | return count; 106 | } 107 | 108 | // 将所有规则形成Netlink回包 109 | void* formAllIPRules(unsigned int *len) { 110 | struct KernelResponseHeader *head; 111 | struct IPRule *now; 112 | void *mem,*p; 113 | unsigned int count; 114 | read_lock(&ipRuleLock); 115 | for(now=ipRuleHead,count=0;now!=NULL;now=now->nx,count++); 116 | *len = sizeof(struct KernelResponseHeader) + sizeof(struct IPRule)*count; 117 | mem = kzalloc(*len, GFP_ATOMIC); 118 | if(mem == NULL) { 119 | printk(KERN_WARNING "[fw rules] kzalloc fail.\n"); 120 | read_unlock(&ipRuleLock); 121 | return NULL; 122 | } 123 | head = (struct KernelResponseHeader *)mem; 124 | head->bodyTp = RSP_IPRules; 125 | head->arrayLen = count; 126 | for(now=ipRuleHead,p=(mem + sizeof(struct KernelResponseHeader));now!=NULL;now=now->nx,p=p+sizeof(struct IPRule)) 127 | memcpy(p, now, sizeof(struct IPRule)); 128 | read_unlock(&ipRuleLock); 129 | return mem; 130 | } 131 | 132 | bool matchOneRule(struct IPRule *rule, 133 | unsigned int sip, unsigned int dip, unsigned short sport, unsigned int dport, u_int8_t proto) { 134 | return (isIPMatch(sip,rule->saddr,rule->smask) && 135 | isIPMatch(dip,rule->daddr,rule->dmask) && 136 | (sport >= ((unsigned short)(rule->sport >> 16)) && sport <= ((unsigned short)(rule->sport & 0xFFFFu))) && 137 | (dport >= ((unsigned short)(rule->dport >> 16)) && dport <= ((unsigned short)(rule->dport & 0xFFFFu))) && 138 | (rule->protocol == IPPROTO_IP || rule->protocol == proto)); 139 | } 140 | 141 | // 进行过滤规则匹配,isMatch存储是否匹配到规则 142 | struct IPRule matchIPRules(struct sk_buff *skb, int *isMatch) { 143 | struct IPRule *now,ret; 144 | unsigned short sport,dport; 145 | struct iphdr *header = ip_hdr(skb); 146 | *isMatch = 0; 147 | getPort(skb,header,&sport,&dport); 148 | read_lock(&ipRuleLock); 149 | for(now=ipRuleHead;now!=NULL;now=now->nx) { 150 | if(matchOneRule(now,ntohl(header->saddr),ntohl(header->daddr),sport,dport,header->protocol)) { 151 | ret = *now; 152 | *isMatch = 1; 153 | break; 154 | } 155 | } 156 | read_unlock(&ipRuleLock); 157 | return ret; 158 | } -------------------------------------------------------------------------------- /src/kernel_mod/hooks/hook_main.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | #include "helper.h" 3 | #include "hook.h" 4 | 5 | unsigned int DEFAULT_ACTION = NF_ACCEPT; 6 | 7 | unsigned int hook_main(void *priv,struct sk_buff *skb,const struct nf_hook_state *state) { 8 | struct IPRule rule; 9 | struct connNode *conn; 10 | unsigned short sport, dport; 11 | unsigned int sip, dip, action = DEFAULT_ACTION; 12 | int isMatch = 0, isLog = 1; // 默认记录日志 13 | // 初始化 14 | struct iphdr *header = ip_hdr(skb); 15 | getPort(skb,header,&sport,&dport); 16 | sip = ntohl(header->saddr); 17 | dip = ntohl(header->daddr); 18 | // 查询是否有已有连接 19 | conn = hasConn(sip, dip, sport, dport); 20 | if(conn != NULL) { 21 | if(conn->needLog) // 记录日志 22 | addLogBySKB(action, skb); 23 | return NF_ACCEPT; 24 | } 25 | // 匹配规则 26 | rule = matchIPRules(skb, &isMatch); 27 | if(isMatch) { // 匹配到了一条规则 28 | printk(KERN_DEBUG "[fw netfilter] patch rule %s.\n", rule.name); 29 | action = (rule.action==NF_ACCEPT) ? NF_ACCEPT : NF_DROP; 30 | if(rule.log) { // 记录日志 31 | isLog = 1; 32 | addLogBySKB(action, skb); 33 | } 34 | } 35 | // 更新连接池 36 | if(action == NF_ACCEPT) { 37 | addConn(sip,dip,sport,dport,header->protocol,isLog); 38 | } 39 | return action; 40 | } -------------------------------------------------------------------------------- /src/kernel_mod/hooks/hook_nat.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | #include "helper.h" 3 | #include "hook.h" 4 | 5 | unsigned int hook_nat_in(void *priv,struct sk_buff *skb,const struct nf_hook_state *state) { 6 | struct connNode *conn; 7 | struct NATRecord record; 8 | unsigned short sport, dport; 9 | unsigned int sip, dip; 10 | u_int8_t proto; 11 | struct tcphdr *tcpHeader; 12 | struct udphdr *udpHeader; 13 | int hdr_len, tot_len; 14 | // 初始化 15 | struct iphdr *header = ip_hdr(skb); 16 | getPort(skb,header,&sport,&dport); 17 | sip = ntohl(header->saddr); 18 | dip = ntohl(header->daddr); 19 | proto = header->protocol; 20 | // 查连接池 NAT_TYPE_DEST 21 | conn = hasConn(sip, dip, sport, dport); 22 | if(conn == NULL) { // 不应出现连接表中不存在的情况 23 | printk(KERN_WARNING "[fw nat] (in)get a connection that is not in the connection pool!\n"); 24 | return NF_ACCEPT; 25 | } 26 | // 无记录->返回 27 | if(conn->natType != NAT_TYPE_DEST) { 28 | return NF_ACCEPT; 29 | } 30 | // 转换目的地址+端口 31 | record = conn->nat; 32 | header->daddr = htonl(record.daddr); 33 | hdr_len = header->ihl * 4; 34 | tot_len = ntohs(header->tot_len); 35 | header->check = 0; 36 | header->check = ip_fast_csum(header, header->ihl); 37 | switch(proto) { 38 | case IPPROTO_TCP: 39 | tcpHeader = (struct tcphdr *)(skb->data + (header->ihl * 4)); 40 | tcpHeader->dest = htons(record.dport); 41 | tcpHeader->check = 0; 42 | skb->csum = csum_partial((unsigned char *)tcpHeader, tot_len - hdr_len, 0); 43 | tcpHeader->check = csum_tcpudp_magic(header->saddr, header->daddr, 44 | tot_len - hdr_len, header->protocol, skb->csum); 45 | break; 46 | case IPPROTO_UDP: 47 | udpHeader = (struct udphdr *)(skb->data + (header->ihl * 4)); 48 | udpHeader->dest = htons(record.dport); 49 | udpHeader->check = 0; 50 | skb->csum = csum_partial((unsigned char *)udpHeader, tot_len - hdr_len, 0); 51 | udpHeader->check = csum_tcpudp_magic(header->saddr, header->daddr, 52 | tot_len - hdr_len, header->protocol, skb->csum); 53 | break; 54 | case IPPROTO_ICMP: 55 | default: 56 | break; 57 | } 58 | return NF_ACCEPT; 59 | } 60 | 61 | unsigned int hook_nat_out(void *priv,struct sk_buff *skb,const struct nf_hook_state *state) { 62 | struct connNode *conn,*reverseConn; 63 | struct NATRecord record; 64 | int isMatch, hdr_len, tot_len; 65 | struct tcphdr *tcpHeader; 66 | struct udphdr *udpHeader; 67 | u_int8_t proto; 68 | unsigned int sip, dip; 69 | unsigned short sport, dport; 70 | // 初始化 71 | struct iphdr *header = ip_hdr(skb); 72 | getPort(skb,header,&sport,&dport); 73 | sip = ntohl(header->saddr); 74 | dip = ntohl(header->daddr); 75 | proto = header->protocol; 76 | // 查连接池 NAT_TYPE_SRC 77 | conn = hasConn(sip, dip, sport, dport); 78 | if(conn == NULL) { // 不应出现连接表中不存在的情况 79 | printk(KERN_WARNING "[fw nat] (out)get a connection that is not in the connection pool!\n"); 80 | return NF_ACCEPT; 81 | } 82 | // 确定NAT记录 83 | if(conn->natType == NAT_TYPE_SRC) { // 已有 84 | record = conn->nat; 85 | } else { 86 | unsigned short newPort = 0; 87 | struct NATRecord *rule = matchNATRule(sip, dip, &isMatch); 88 | if(!isMatch || rule == NULL) { // 不符合NAT规则,无需NAT 89 | return NF_ACCEPT; 90 | } 91 | // 新建NAT记录 92 | if(sport != 0) { 93 | newPort = getNewNATPort(*rule); 94 | if(newPort == 0) { // 获取新端口失败,放弃NAT 95 | printk(KERN_WARNING "[fw nat] get new port failed!\n"); 96 | return NF_ACCEPT; 97 | } 98 | } 99 | record = genNATRecord(sip, rule->daddr, sport, newPort); 100 | // 记录在原连接中 101 | setConnNAT(conn, record, NAT_TYPE_SRC); 102 | rule->nowPort = newPort; 103 | } 104 | // 寻找反向连接 105 | reverseConn = hasConn(dip, record.daddr, dport, record.dport); 106 | if(reverseConn == NULL) { // 新建反向连接入连接池 107 | reverseConn = addConn(dip, record.daddr, dport, record.dport, proto, 0); 108 | if(reverseConn == NULL) { // 创建反向连接失败,放弃NAT 109 | printk(KERN_WARNING "[fw nat] add reverse connection failed!\n"); 110 | return NF_ACCEPT; 111 | } 112 | setConnNAT(reverseConn, genNATRecord(record.daddr, sip, record.dport, sport), NAT_TYPE_DEST); 113 | } 114 | addConnExpires(reverseConn, CONN_EXPIRES * CONN_NAT_TIMES); // 更新超时时间 115 | addConnExpires(conn, CONN_EXPIRES * CONN_NAT_TIMES); // 更新超时时间 116 | // 转换源地址+端口 117 | header->saddr = htonl(record.daddr); 118 | hdr_len = header->ihl * 4; 119 | tot_len = ntohs(header->tot_len); 120 | header->check = 0; 121 | header->check = ip_fast_csum(header, header->ihl); 122 | switch(proto) { 123 | case IPPROTO_TCP: 124 | tcpHeader = (struct tcphdr *)(skb->data + (header->ihl * 4)); 125 | tcpHeader->source = htons(record.dport); 126 | tcpHeader->check = 0; 127 | skb->csum = csum_partial((unsigned char *)tcpHeader, tot_len - hdr_len, 0); 128 | tcpHeader->check = csum_tcpudp_magic(header->saddr, header->daddr, 129 | tot_len - hdr_len, header->protocol, skb->csum); 130 | break; 131 | case IPPROTO_UDP: 132 | udpHeader = (struct udphdr *)(skb->data + (header->ihl * 4)); 133 | udpHeader->source = htons(record.dport); 134 | udpHeader->check = 0; 135 | skb->csum = csum_partial((unsigned char *)udpHeader, tot_len - hdr_len, 0); 136 | udpHeader->check = csum_tcpudp_magic(header->saddr, header->daddr, 137 | tot_len - hdr_len, header->protocol, skb->csum); 138 | break; 139 | case IPPROTO_ICMP: 140 | default: 141 | break; 142 | } 143 | return NF_ACCEPT; 144 | } -------------------------------------------------------------------------------- /src/kernel_mod/include/dependency.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEPENDENCY_H 2 | #define _DEPENDENCY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #endif -------------------------------------------------------------------------------- /src/kernel_mod/include/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef _NETLINK_HELPER_H 2 | #define _NETLINK_HELPER_H 3 | 4 | #include "dependency.h" 5 | 6 | // ---- APP 与 Kernel 通用协议 ------ 7 | #define MAXRuleNameLen 11 8 | 9 | #define REQ_GETAllIPRules 1 10 | #define REQ_ADDIPRule 2 11 | #define REQ_CHANGEIPRule 16 12 | #define REQ_DELIPRule 3 13 | #define REQ_SETAction 4 14 | #define REQ_GETAllIPLogs 5 15 | #define REQ_GETAllConns 6 16 | #define REQ_ADDNATRule 7 17 | #define REQ_DELNATRule 8 18 | #define REQ_GETNATRules 9 19 | 20 | #define RSP_Only_Head 10 21 | #define RSP_MSG 11 22 | #define RSP_IPRules 12 // body为IPRule[] 23 | #define RSP_IPLogs 13 // body为IPlog[] 24 | #define RSP_NATRules 14 // body为NATRecord[] 25 | #define RSP_ConnLogs 15 // body为ConnLog[] 26 | 27 | struct IPRule { 28 | char name[MAXRuleNameLen+1]; 29 | unsigned int saddr; 30 | unsigned int smask; 31 | unsigned int daddr; 32 | unsigned int dmask; 33 | unsigned int sport; // 源端口范围 高2字节为最小 低2字节为最大 34 | unsigned int dport; // 目的端口范围 同上 35 | u_int8_t protocol; 36 | unsigned int action; 37 | unsigned int log; 38 | struct IPRule* nx; 39 | }; 40 | 41 | struct IPLog { 42 | long tm; 43 | unsigned int saddr; 44 | unsigned int daddr; 45 | unsigned short sport; 46 | unsigned short dport; 47 | u_int8_t protocol; 48 | unsigned int len; 49 | unsigned int action; 50 | struct IPLog* nx; 51 | }; 52 | 53 | struct NATRecord { // NAT 记录 or 规则(源IP端口转换) 54 | unsigned int saddr; // 记录:原始IP | 规则:原始源IP 55 | unsigned int smask; // 记录:无作用 | 规则:原始源IP掩码 56 | unsigned int daddr; // 记录:转换后的IP | 规则:NAT 源IP 57 | 58 | unsigned short sport; // 记录:原始端口 | 规则:最小端口范围 59 | unsigned short dport; // 记录:转换后的端口 | 规则:最大端口范围 60 | unsigned short nowPort; // 记录:当前使用端口 | 规则:无作用 61 | struct NATRecord* nx; 62 | }; 63 | 64 | struct ConnLog { 65 | unsigned int saddr; 66 | unsigned int daddr; 67 | unsigned short sport; 68 | unsigned short dport; 69 | u_int8_t protocol; 70 | int natType; 71 | struct NATRecord nat; // NAT记录 72 | }; 73 | 74 | struct APPRequest { 75 | unsigned int tp; 76 | char ruleName[MAXRuleNameLen+1]; 77 | int num; 78 | union { 79 | struct IPRule ipRule; 80 | struct NATRecord natRule; 81 | unsigned int defaultAction; 82 | unsigned int num; 83 | } msg; 84 | }; 85 | 86 | struct KernelResponseHeader { 87 | unsigned int bodyTp; 88 | unsigned int arrayLen; 89 | }; 90 | 91 | #define NAT_TYPE_NO 0 92 | #define NAT_TYPE_SRC 1 93 | #define NAT_TYPE_DEST 2 94 | 95 | // ----- netlink 相关 ----- 96 | #include 97 | 98 | // netlink 协议号 99 | #define NETLINK_MYFW 17 100 | 101 | struct sock *netlink_init(void); 102 | void netlink_release(void); 103 | int nlSend(unsigned int pid, void *data, unsigned int len); 104 | 105 | // ----- 应用交互相关 ------- 106 | int dealAppMessage(unsigned int pid, void *msg, unsigned int len); 107 | void* formAllIPRules(unsigned int *len); 108 | struct IPRule * addIPRuleToChain(char after[], struct IPRule rule); 109 | struct IPRule * changeIPRuleOfChain(int key, struct IPRule rule); 110 | int delIPRuleFromChain(char name[]); 111 | void* formAllIPLogs(unsigned int num, unsigned int *len); 112 | void* formAllConns(unsigned int *len); 113 | struct NATRecord * addNATRuleToChain(struct NATRecord rule); 114 | int delNATRuleFromChain(int num); 115 | void* formAllNATRules(unsigned int *len); 116 | 117 | // ----- netfilter相关 ----- 118 | // 最大缓存日志长度 119 | #define MAX_LOG_LEN 1000 120 | 121 | struct IPRule matchIPRules(struct sk_buff *skb, int *isMatch); 122 | int addLog(struct IPLog log); 123 | int addLogBySKB(unsigned int action, struct sk_buff *skb); 124 | 125 | // ----- 连接池相关 -------- 126 | #include 127 | 128 | #define CONN_NEEDLOG 0x10 129 | #define CONN_MAX_SYM_NUM 3 130 | #define CONN_EXPIRES 7 // 新建连接或已有连接刷新时的存活时长(秒) 131 | #define CONN_NAT_TIMES 10 // NAT的超时时间倍率 132 | #define CONN_ROLL_INTERVAL 5 // 定期清理超时连接的时间间隔(秒) 133 | 134 | typedef unsigned int conn_key_t[CONN_MAX_SYM_NUM]; // 连接标识符,用于标明一个连接,可比较 135 | 136 | typedef struct connNode { 137 | struct rb_node node; 138 | conn_key_t key; // 连接标识符 139 | unsigned long expires; // 超时时间 140 | u_int8_t protocol; // 协议,仅用于向用户展示 141 | u_int8_t needLog; // 是否记录日志,仅用于hook 142 | 143 | struct NATRecord nat; // 该连接对应的NAT记录 144 | int natType; // NAT 转换类型 145 | }connNode; 146 | 147 | #define timeFromNow(plus) (jiffies + ((plus) * HZ)) 148 | 149 | void conn_init(void); 150 | void conn_exit(void); 151 | struct connNode *hasConn(unsigned int sip, unsigned int dip, unsigned short sport, unsigned short dport); 152 | struct connNode *addConn(unsigned int sip, unsigned int dip, unsigned short sport, unsigned short dport, u_int8_t proto, u_int8_t log); 153 | bool matchOneRule(struct IPRule *rule, unsigned int sip, unsigned int dip, unsigned short sport, unsigned int dport, u_int8_t proto); 154 | int eraseConnRelated(struct IPRule rule); 155 | void addConnExpires(struct connNode *node, unsigned int plus); 156 | 157 | // ---- NAT 初始操作相关 ---- 158 | 159 | int setConnNAT(struct connNode *node, struct NATRecord record, int natType); 160 | struct NATRecord *matchNATRule(unsigned int sip, unsigned int dip, int *isMatch); 161 | unsigned short getNewNATPort(struct NATRecord rule); 162 | struct NATRecord genNATRecord(unsigned int preIP, unsigned int afterIP, unsigned short prePort, unsigned short afterPort); 163 | 164 | #endif -------------------------------------------------------------------------------- /src/kernel_mod/include/hook.h: -------------------------------------------------------------------------------- 1 | #ifndef _HOOK_H 2 | #define _HOOK_H 3 | 4 | unsigned int hook_main(void *priv,struct sk_buff *skb,const struct nf_hook_state *state); 5 | 6 | unsigned int hook_nat_in(void *priv,struct sk_buff *skb,const struct nf_hook_state *state); 7 | unsigned int hook_nat_out(void *priv,struct sk_buff *skb,const struct nf_hook_state *state); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/kernel_mod/include/tools.h: -------------------------------------------------------------------------------- 1 | #ifndef _TOOLS_H 2 | #define _TOOLS_H 3 | 4 | #include "dependency.h" 5 | 6 | void getPort(struct sk_buff *skb, struct iphdr *hdr, unsigned short *src_port, unsigned short *dst_port); 7 | bool isIPMatch(unsigned int ipl, unsigned int ipr, unsigned int mask); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/kernel_mod/mod_main.c: -------------------------------------------------------------------------------- 1 | #include "dependency.h" 2 | #include "hook.h" 3 | #include "helper.h" 4 | 5 | static struct nf_hook_ops nfop_in={ 6 | .hook = hook_main, 7 | .pf = PF_INET, 8 | .hooknum = NF_INET_PRE_ROUTING, 9 | .priority = NF_IP_PRI_FIRST 10 | }; 11 | 12 | static struct nf_hook_ops nfop_out={ 13 | .hook = hook_main, 14 | .pf = PF_INET, 15 | .hooknum = NF_INET_POST_ROUTING, 16 | .priority = NF_IP_PRI_FIRST 17 | }; 18 | 19 | static struct nf_hook_ops natop_in={ 20 | .hook = hook_nat_in, 21 | .pf = PF_INET, 22 | .hooknum = NF_INET_PRE_ROUTING, 23 | .priority = NF_IP_PRI_NAT_DST 24 | }; 25 | 26 | static struct nf_hook_ops natop_out={ 27 | .hook = hook_nat_out, 28 | .pf = PF_INET, 29 | .hooknum = NF_INET_POST_ROUTING, 30 | .priority = NF_IP_PRI_NAT_SRC 31 | }; 32 | 33 | static int mod_init(void){ 34 | printk("my firewall module loaded.\n"); 35 | nf_register_net_hook(&init_net,&nfop_in); 36 | nf_register_net_hook(&init_net,&nfop_out); 37 | nf_register_net_hook(&init_net,&natop_in); 38 | nf_register_net_hook(&init_net,&natop_out); 39 | netlink_init(); 40 | conn_init(); 41 | return 0; 42 | } 43 | 44 | static void mod_exit(void){ 45 | printk("my firewall module exit.\n"); 46 | nf_unregister_net_hook(&init_net,&nfop_in); 47 | nf_unregister_net_hook(&init_net,&nfop_out); 48 | nf_unregister_net_hook(&init_net,&natop_in); 49 | nf_unregister_net_hook(&init_net,&natop_out); 50 | netlink_release(); 51 | conn_exit(); 52 | } 53 | 54 | MODULE_LICENSE("GPL"); 55 | MODULE_AUTHOR("ssd"); 56 | module_init(mod_init); 57 | module_exit(mod_exit); 58 | -------------------------------------------------------------------------------- /src/kernel_mod/tools.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | 3 | void getPort(struct sk_buff *skb, struct iphdr *hdr, unsigned short *src_port, unsigned short *dst_port){ 4 | struct tcphdr *tcpHeader; 5 | struct udphdr *udpHeader; 6 | switch(hdr->protocol){ 7 | case IPPROTO_TCP: 8 | //printk("TCP protocol\n"); 9 | tcpHeader = (struct tcphdr *)(skb->data + (hdr->ihl * 4)); 10 | *src_port = ntohs(tcpHeader->source); 11 | *dst_port = ntohs(tcpHeader->dest); 12 | break; 13 | case IPPROTO_UDP: 14 | //printk("UDP protocol\n"); 15 | udpHeader = (struct udphdr *)(skb->data + (hdr->ihl * 4)); 16 | *src_port = ntohs(udpHeader->source); 17 | *dst_port = ntohs(udpHeader->dest); 18 | break; 19 | case IPPROTO_ICMP: 20 | default: 21 | //printk("other protocol\n"); 22 | *src_port = 0; 23 | *dst_port = 0; 24 | break; 25 | } 26 | } 27 | 28 | bool isIPMatch(unsigned int ipl, unsigned int ipr, unsigned int mask) { 29 | return (ipl & mask) == (ipr & mask); 30 | } -------------------------------------------------------------------------------- /参考资料/Netfilter-Firewall_README.md: -------------------------------------------------------------------------------- 1 | # Linux下状态检测防火墙的设计与实现 2 | 3 | ## 0x00 简介 4 | 5 | 本实验为2021年华中科技大学网络空间安全学院《网络安全课程设计》的实验,要求实现一个带NAT功能的状态检测防火墙(NAT我没有实现)。这份课设本来就有参考之前的学长学姐的代码,虽然我写的很垃但还是贡献出来,送给想划水的同学,希望能帮到你,也希望你写好之后也能上传分享给以后的学弟学妹 :) 6 | 7 | ## 0x01 实验目的和要求 8 | 9 | **实验目的** 10 | 11 | - 结合理论课程学习,深入理解计算机网络安全的基本 原理与协议,巩固计算机网络安全基本理论知识 12 | 13 | - 熟练掌握计算机网络编程方法,拓展学生的应用能力 14 | 15 | - 加强对网络协议栈的理解 16 | 17 | - 提高分析、设计软件系统以及编写文档的能力 18 | 19 | - 培养团队合作能力。 20 | 21 | **实验要求** 22 | 23 | - 正确理解题意 24 | 25 | - 具有良好的编程规范和适当的注释 26 | 27 | - 有详细的文档,文档中应包括设计题目涉及的基础知识、 设计思路、程序流程图、程序清单、开发中遇到的问题及解决方法、设计中待解决的问题及改进方向 28 | 29 | **任务要求:** 30 | 31 | 1. 系统运行 32 | - 系统启动以后插入模块,防火墙以内核模块方式运行 33 | - 应用程序读取配置,向内核写入规则,报文到达,按照规则进行处理 34 | 2. 界面 35 | - 采用图形或者命令行方式进行规则配置,界面友好 36 | 3. 功能要求 37 | - 能对TCP、UDP、ICMP协议的报文进行状态分析和过滤 38 | - 每一条过滤规则至少包含:报文的源IP(带掩码的网络地址)、目的IP(带掩码的网络地址)、源端口、目的端口、 协议、动作(禁止/允许),是否记录日志 39 | - 过滤规则可以进行添加、删除、保存,配置的规则能立即生效 40 | - 过滤日志可以查看 41 | - 具有NAT功能,转换地址分按接口地址转换和指定地址转 换(能实现源或者目的地址转换的任一种即可) 42 | - 能查看所有连接状态信息 43 | 4. 测试 44 | 45 | - 测试系统是否符合设计要求 46 | - 系统运行稳定 47 | - 性能分析 48 | 49 | ## 0x02 实验原理 50 | 51 | ### 状态检测技术 52 | 53 | 状态检测技术是防火墙近几年才应用的新技术。传统的包过滤防火墙只是通过检测IP包头的相关信息来决定数据流的通过还是拒绝,而状态检测技术采用的是一种基于连接的状态检测机制,将属于同一连接的所有包作为一个整体的数据流看待,构成连接状态表,通过规则表与状态表的共同配合,对表中的各个连接状态因素加以识别。这些状态信息可由状态检测表存储,状态检测表包括所有通过防火墙的连接,并维护所有连接。每条连接的信息包括源地址、目的地址、协议类型、协议相关信息(如TCP/UDP协议的端口、ICMP协议的ID号)、连接状态(如TCP连接状态)和超时时间(若进行NAT转换,还需记录转换前后地址端口信息)等。这里动态连接状态表中的记录可以是以前的通信信息,也可以是其他相关应用程序的信息,因此,与传统包过滤防火墙的静态过滤规则表相比,它具有更好的灵活性和安全性。 54 | 55 | 对于TCP连接,一次连接以3次握手开始,以4次挥手或Reset报文结束。由于TCP协议是询问-应答类型的可靠数据传输协议,故TCP连接存在状态,对于同一连接的报文,只需对其握手报文进行规则匹配即可,若允许通过,则同一TCP连接的所有报文均可通过,无需遍历规则表。 56 | 57 | ![TCP连接处理流程](images/clip_image002.jpg) 58 | 59 | 对于非TCP连接,防火墙为其建立虚拟连接。 60 | 61 | UDP:一方发出UDP包后,如DNS请求,防火墙会将从目的地址和端口返回的、到达源地址源端口的包作为状态相关的包而允许通过。 62 | 63 | ICMP:信息查询报文,如ping(echo)包,其状态相关包就是来自目的地址的echo reply包,而没有与echo包对应的echo reply包则认为是状态非法的。 64 | 65 | ### Linux Netfilter架构 66 | 67 | Netfilter 是 Linux 内核中进行数据包过滤,连接跟踪(Connect Track),网络地址转换(NAT)等功能的主要实现框架;该框架在网络协议栈处理数据包的关键流程中定义了一系列钩子点(Hook 点),并在这些钩子点中注册一系列函数对数据包进行处理。这些注册在钩子点的函数即为设置在网络协议栈内的数据包通行策略,也就意味着,这些函数可以决定内核是接受还是丢弃某个数据包,换句话说,这些函数的处理结果决定了这些网络数据包的“命运”。 68 | 69 | Netfilter框架在网络协议各层的各个hook点如下图所示。 70 | 71 | ![Netfilter框架hook点](images/clip_image002-16414060761671.jpg) 72 | 73 | ### Linux字符设备驱动 74 | 75 | 字符设备:是指只能一个字节一个字节进行读写操作的设备,不能随机读取设备中的某一数据、读取数据要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED等。 76 | 77 | 字符设备可以通过文件节点来访问,比如/dev/tty1和/dev/lp0等。这些设备文件和普通文件之间的唯一差别在于对普通文件的访问可以前后移动访问位置,而大多数字符设备是一个只能顺序访问的数据通道。然而,也存在具有数据区特性的字符设备,访问它们时可前后移动访问位置。例如framebuffer就是这样的一个设备,app可以用mmap或lseek访问抓取的整个图像。 78 | 79 | 如图,在Linux内核代码中: 80 | 81 | - 使用struct cdev结构体来抽象一个字符设备; 82 | - 通过一个dev_t类型的设备号(分为主(major)、次设备号(minor))一确定字符设备唯一性; 83 | - 通过struct file_operations类型的操作方法集来定义字符设备提供个VFS的接口函数。 84 | 85 | ![Linux字符设备驱动](images/clip_image002.png) 86 | 87 | ## 0x03 实验环境和采用的工具 88 | 89 | 操作系统:Ubuntu14.04 90 | 91 | 内核版本:Linux 4.4.0-142-generic 92 | 93 | 编译环境:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) 94 | 95 | ## 0x04 系统设计 96 | 97 | ### 系统静态结构设计 98 | 99 | - 内核模块设计 100 | 101 | 内核模块由访问控制处理逻辑和字符设备驱动构成。访问控制处理逻辑负责对报文进行过滤分析,实现状态监测;字符设备驱动负责内核态与用户态的交互。 102 | 103 | 访问控制处理逻辑维护了规则表、日志表、HASH表与连接链、NAT表。其中,规则表负责记录规则信息,包括七元组(五元组+IP掩码)、动作、以及是否记录日志等;日志表负责记录规则与连接信息;HASH表与连接链共同维护连接信息,包括连接双方的网络信息、剩余超时时间等;NAT表负责维护NAT地址转换。 104 | 105 | hook_in和hook_out两个Hook函数维护了连接链和HASH表。通过查询规则表的访问控制规则决定是否允许报文通过,同时更新连接链和Hash表的状态信息,并且对匹配的规则记录日志。 106 | 107 | 字符设备驱动部分通过对字符设备的操作实现内核模块与用户模块的信息传递。其中,.read操作负责根据操作码,将内核模块所维护的连接信息、日志信息以及NAT信息发送给字符设备供用户模块读取;.write操作负责从字符设备读取操作码,实现规则载入以及调用.read实现信息输出。 108 | 109 | - 用户模块设计 110 | 111 | 用户模块由C++实现,完成了规则的维护(包括存储、读取、新增、删除、以及载入到内核)、连接信息的获取、日志获取。 112 | 113 | 用户模块项目结构如下所示,将内核模块的5个重要数据结构抽象成类。 114 | 115 | 内核模块的规则信息在用户模块抽象成Rule类。主要实现新规则的构造、规则信息的格式转换,以及规则展示。 116 | 117 | 内核模块的日志信息在用户模块抽象成Log类。主要实现五元组信息的记录以及日志信息的展示。 118 | 119 | 内核模块的HASH表与连接链在用户模块共同抽象成Connection类。主要记录了连接状态信息,除五元组外还包含连接剩余时间。 120 | 121 | 内核模块的NAT信息在用户模块抽象成NAT类。主要实现NAT信息的记录、修改与展示。 122 | 123 | 除了规则Rule类之外,剩余三个类都是直接与内核模块进行信息交互,信息流通方向主要是从内核模块到用户模块单向流通。但Rule信息采用了本地存储-远程提交的方法,通过在用户模块建立本地数据库对规则进行增、删、查、改的统一管理,再通过commit操做将整个规则表发送给内核模块,有效的减少了不必要的信息流动,增加了系统工作效率。 124 | 125 | ### 系统核心数据流程 126 | 127 | hook_in与hook_out两个hook函数获取到流量包后,统一交付给check_pkg函数处理。check_pkg函数首先根据流量包的五元组信息检查该流量包是否属于已经建立的连接。若属于,则直接放行,并更新所属连接的存活时间;否则,将根据流量包的类型(TCP/UDP/ICMP/其它)以及规则表来确定是创建新连接还是拒绝流量包通过。 128 | 129 | ## 0x05 系统详细设计 130 | 131 | ### 关键数据结构设计 132 | 133 | - 内核模块数据结构 134 | 135 | 1. 规则表 136 | 137 | 规则表为struct rule结构体的数组,同时通过static int 类型的rule_num变量来维护规则表的大小。 138 | 139 | struct rule结构体定义如下: 140 | 141 | | **类型** | **变量** | **描述** | 142 | | -------- | -------- | ---------------------------- | 143 | | unsigned | src_ip | 源IP | 144 | | unsigned | dst_ip | 目的IP | 145 | | unsigned | src_mask | 源IP掩码 | 146 | | unsigned | dst_mask | 目的IP掩码 | 147 | | int | src_port | 源端口 | 148 | | int | dst_port | 目的端口 | 149 | | int | protocol | 协议号 | 150 | | int | action | 动作(0丢弃,1通过) | 151 | | int | log | 是否记录日志(0忽略,1记录) | 152 | 153 | 2. 日志表 154 | 155 | 日志表为struct log结构体的数组,同时通过static int 类型的log_num变量来维护日志表的大小。 156 | 157 | struct log结构体定义如下: 158 | 159 | | **类型** | **变量** | **描述** | 160 | | -------- | -------- | -------------------- | 161 | | unsigned | src_ip | 源IP | 162 | | unsigned | dst_ip | 目的IP | 163 | | int | src_port | 源端口 | 164 | | int | dst_port | 目的端口 | 165 | | int | protocol | 协议号 | 166 | | int | action | 动作(0丢弃,1通过) | 167 | 168 | 3. 连接链与HASH表 169 | 170 | 连接链为struct connection结构体的链表,表头结点为空结点conHead。每个结点包括该连接的基本五元组信息以及一个用于定位到HASH表的索引字段。 171 | 172 | struct connection结构体定义如下: 173 | 174 | | **类型** | **变量** | **描述** | 175 | | ------------------- | -------- | -------------- | 176 | | unsigned | src_ip | 源IP | 177 | | unsigned | dst_ip | 目的IP | 178 | | int | src_port | 源端口 | 179 | | int | dst_port | 目的端口 | 180 | | int | protocol | 协议号 | 181 | | int | index | HASH索引 | 182 | | struct connection\* | next | 下一个连接结点 | 183 | 184 | HASH表为一个字符数组,记录了连接链中每个连接的剩余存活时间。为了实现HASH表的功能,设计了一个HASH函数,将每个连接的五元组信息作为HASH函数的关键字映射到HASH表中。 185 | 186 | 连接链与HASH表的映射关系如下图所示: 187 | 188 | ![连接链到HASH表的映射](images/clip_image002-16414063769822.jpg) 189 | 190 | - 用户模块数据结构 191 | 192 | 1. Rule类 193 | 194 | Rule类对应内核模块的规则表,提供对规则信息提取与操做多个接口。 195 | 196 | | 变量 | 类型 | 描述 | 197 | | -------- | -------- | ------------ | 198 | | src_ip | unsigned | 源IP | 199 | | dst_ip | unsigned | 目的IP | 200 | | src_mark | unsigned | 源IP掩码 | 201 | | dst_mark | unsigned | 目的IP掩码 | 202 | | src_port | int | 源端口 | 203 | | dst_port | int | 目的端口 | 204 | | protocol | int | 协议号 | 205 | | action | int | 是否通过 | 206 | | log | int | 是否记录日志 | 207 | 208 | | 方法 | 返回值 | 描述 | 209 | | ---------------------------- | -------- | --------------------------------------------------------- | 210 | | Rule() | 构造函数 | 创建一个新的Rule对象 | 211 | | Rule(char* data, int offset) | 构造函数 | 创建一个新的Rule对象(从data指向地址+offset偏移处获取数据) | 212 | | srcIP(std::string ip_str) | void | 将对象的src_ip修改为ip_str所指向的IP | 213 | | dstIP(std::string ip_str) | void | 将对象的dst_ip修改为ip_str所指向的IP | 214 | | srcMask(std::string ip_str) | void | 将对象的src_mask修改为ip_str所指向的掩码 | 215 | | dstMask(std::string ip_str) | void | 将对象的dst_mask修改为ip_str所指向的掩码 | 216 | | print() | void | 将对象按标准格式打印 | 217 | 218 | 2. Log类 219 | 220 | Log类对应内核模块的日志表,提供对日志信息提取与操做多个接口。 221 | 222 | | 变量 | 类型 | 描述 | 223 | | -------- | -------- | -------- | 224 | | src_ip | unsigned | 源IP | 225 | | dst_ip | unsigned | 目的IP | 226 | | src_port | int | 源端口 | 227 | | dst_port | int | 目的端口 | 228 | | protocol | int | 协议号 | 229 | | action | int | 是否通过 | 230 | 231 | | 方法 | 返回值 | 描述 | 232 | | --------------------------- | -------- | -------------------------------------------------------- | 233 | | Log(char *data, int offset) | 构造函数 | 创建一个新的Log对象(从data指向地址+offset偏移处获取数据) | 234 | | print() | void | 将对象按标准格式打印 | 235 | 236 | 3. Connection类 237 | 238 | Connection类对应内核模块的连接表,提供对连接信息提取与操做多个接口。 239 | 240 | | 变量 | 类型 | 描述 | 241 | | -------- | -------- | ------------ | 242 | | src_ip | unsigned | 源IP | 243 | | dst_ip | unsigned | 目的IP | 244 | | src_port | int | 源端口 | 245 | | dst_port | int | 目的端口 | 246 | | protocol | int | 协议号 | 247 | | time | int | 剩余存活时间 | 248 | 249 | | 方法 | 返回值 | 描述 | 250 | | ---------------------------------- | -------- | ------------------------------------------------------------ | 251 | | Connection(char *data, int offset) | 构造函数 | 创建一个新的Connection对象(从data指向地址+offset偏移处获取数据) | 252 | | print() | void | 将对象按标准格式打印 | 253 | 254 | 4. 其它功能函数 255 | 256 | | 返回值 | 函数 | 描述 | 257 | | -------- | --------------------------------- | --------------------------------------------------------- | 258 | | unsigned | strToIp(std::string ip_str) | 将string类型的ip转换为unsigned类型 | 259 | | string | ipToStr(unsigned ip) | 将unsigned类型的ip转换为string类型 | 260 | | int | strToPtc(std::string ptc_str) | 将string类型的ptc(协议名)转换为int类型(协议号) | 261 | | string | ptcToStr(int ptc) | 将int类型的ptc(协议号)转换为string类型(协议名) | 262 | | unsigned | byteToInt(char *byte, int offset) | 将byte指向地址+offset偏移出的字节数据转换为int类型 | 263 | | string | toAct(int x) | 将int类型的action转换为string类型(0-> “Deny”, 1->“Accept” | 264 | | string | toLog(int x) | 将int类型的log转换为string类型(1->“Loged”, 0->“Unloged” | 265 | 266 | ### 关键模块流程设计 267 | 268 | - 内核模块流程 269 | 270 | 1. 报文过滤 271 | 272 | 报文过滤是本次防火墙实验主要需要实现的功能。内核模块中,对于NF_INET_PRE_ROUTING和NF_INET_POST_ROUTING两个hook点分别设置了hook_in与hook_out函数。两个hook函数实际上均是调用名为check_pkg的函数对经过hook点的报文进行判断是否允许通过。 273 | 274 | 在check_pkg函数中,首先提取出报文的五元组信息并判断是否属于已有连接的报文(详见下一节:**状态检测**)。如果属于已有连接则直接放行,否则将报文的五元组信息与规则表中存储的规则逐条匹配来决定能否通过。如果是允许通过的报文,则在连接链与HASH表中创建一个新的连接信息,以便后续的检测使用。 275 | 276 | 报文过滤流程图如下所示: 277 | 278 | ![报文过滤流程](images/clip_image002-16414068716543.jpg) 279 | 280 | 2. 状态检测 281 | 282 | 与普通的包过滤防火墙不同,状态检测防火墙主要基于连接来判断是否允许一个报文通过。 283 | 284 | 为了建立报文与连接的映射,将报文的五元组(源IP,目的IP,源端口,目标端口,协议号)一起作为HASH函数的关键字进行哈希,计算出的HASH值即当前报文对应连接在HASH表中的位置。 285 | 286 | HASH表记录了当前连接的剩余存活时间。如果剩余存活时间为0,说明当前报文未创建连接或连接已超时,内核模块将根据规则表来决定是否为其创建连接;如果剩余时间不为0,说明已经为当前报文创建连接,直接将报文放行并更新剩余时间。 287 | 288 | HASH函数如下所示: 289 | 290 | ![HASH函数](images/clip_image002-16414068912734.jpg) 291 | 292 | 3. 时间控制 293 | 294 | 为了防止资源被占用,需要将连接链中长期没有数据流通的连接删除。Linux内核中提供了一个定时器的功能用于处理周期性工作,我们将使用这个功能来实现时间控制功能。 295 | 296 | 创建一个struct timer_list定时器并绑定到time_out函数。当定时器发生超时时,将会调用time_out函数。 297 | 298 | 在time_out函数中,我们将遍历整条连接链,将所有连接的剩余时间减1。当某个连接的剩余时间被减为0时,表明该链接已经超时,此时将会将该连接结点释放并继续遍历。 299 | 300 | 定时器的超时时间为1秒;连接被创建时初始剩余时间为60。因此当某个连接超过1分钟没有进行数据传输时即会被断开。 301 | 302 | 4. 字符设备驱动 303 | 304 | 为了让内核模块与用户模块进行数据的交互,系统采用字符设备的方法实现了内核模块和用户模块的信息交互。 305 | 306 | 为了区分传递信息的流向与内容,内核模块维护了一个全局操作符变量,并定义了3个操作符: 307 | 308 | | 操作符 | 值 | 含义 | 309 | | -------------- | ---- | ---------------------------- | 310 | | OP_WRITE_RULR | 0 | 内核模块从用户模块取规则信息 | 311 | | OP_GET_CONNECT | 1 | 用户模块从内核模块取连接信息 | 312 | | OP_GET_LOG | 2 | 用户模块从内核模块取日志信息 | 313 | 314 | 每次进行数据交互时,规定总是首先由用户模块向内核模块写入数据,且写入数据的最后一个字节为上述操作符之一。 315 | 316 | 当用户模块写入数据时,判断其操作符。操作符为OP_WRITE_RULR时,内核模块将读取用户模块写入的所有数据,并将数据逐条存入内核模块的规则表中;当操作符为OP_GET_CONNECT或OP_GET_LOG时,内核模块将更新其维护的全局操作符为对应的值; 317 | 318 | 当用户模块读取数据时,由于在上一步的写入操作符过程中更新了其全局操作符,内核模块将根据全局操作符的值来决定是将日志信息还是连接信息写入字符设备,再由用户模块读取。 319 | 320 | 数据传输流程如下图所示: 321 | 322 | ![用户模块与内核模块的数据交换](images/clip_image002-16414069119185.jpg) 323 | 324 | - 用户模块流程 325 | 326 | 用户模块用C++实现。 327 | 328 | ​ 程序运行时,将根据其传入的参数决定功能。具体功能如下图所示: 329 | 330 | ​ ![程序使用方法](images/clip_image002-16414069356946.jpg) 331 | 332 | 1. 规则的增删查改 333 | 334 | 用户模块最重要的功能在于规则的维护,包括输出规则、新建规则与删除规则。 335 | 336 | 不同于日志信息与连接信息主要由内核模块维护,规则信息采取了“本地存储+远程推送”的模式,即:规则的存储、新增、查询、删除都是在本地数据库中进行操作,内核模块只能单向地接受来自用户模块所提交的规则,而不能将规则信息输出到用户模块。这样既能保证每次机器重启时都能保存之前所创建的规则表,又减少了内核模块与用户模块之间频繁的数据传输,增加了系统执行效率。 337 | 338 | 对于新建规则,程序将根据用户输入信息创建一个新的Rule类型的对象,并通过文件读写操作将其保存在本地数据库;对于删除规则,程序将根据索引值在本地数据库中删除对应规则的信息;对于查询规则,程序将把本地数据库中所存储的规则添加到用户模块所维护的规则表中。 339 | 340 | 当本地规则维护完毕之后,用户可使用 `-c/--commit` 参数将规则提交到内核模块。此时,用户模块首先将本地数据库中的规则信息全部加载到用户模块所维护的规则表中。之后用户模块向内核模块所创建的字符设备输入OP_WRITE_RULR操作符,并将用户模块所存储的规则表全部输入到字符设备;内核模块收到OP_WRITE_RULR操作符时,将会开始接受字符设备中的规则表,实现了规则表从用户模块向规则模块的流通。 341 | 342 | ![系统之间信息传输](images/clip_image002-16414069505887.jpg) 343 | 344 | 2. 日志信息打印 345 | 346 | 当使用 `-p l/log` 命令时,程序将打印当前存储的日志信息。 347 | 348 | 当使用 `-p l/log` 命令时,用户模块将会向内核模块所创建的字符设备输入OP_GET_LOG操作符。内核模块收到操作符时,会把其所维护的日志信息输出到字符设备中,用户模块再读取字符设备中所获得的日志信息加载到其所维护的日志表中并打印到屏幕上。 349 | 350 | ![日志信息打印](images/clip_image002-16414069922098.jpg) 351 | 352 | 3. 连接信息打印 353 | 354 | 当使用 `-p c/connection` 命令时,程序将打印当前存储的日志信息。 355 | 356 | 当使用 `-p c/connection` 命令时,用户模块将会向内核模块所创建的字符设备输入OP_GET_CONNECT 操作符。内核模块收到操作符时,会把其所维护的连接信息输出到字符设备中,用户模块再读取字符设备中所获得的连接信息加载到其所维护的连接表中并打印到屏幕上。 357 | 358 | ![连接信息打印](images/clip_image002-16414070055289.jpg) 359 | 360 | ## 0x06 系统测试 361 | 362 | ### 测试环境 363 | 364 | 内网网段为192.168.11.0/24,外网网段为192.168.23.0/24。部署三台虚拟机,分别模拟内网主机、外网主机与防火墙。其中内网主机IP为192.168.11.129,外网主机IP为192.168.23.129,防火墙同时连接内外网负责转发,IP为192.168.11.128(内网)、192.168.23.128(外网) 365 | 366 | ![实验拓扑环境](images/clip_image002-164140710634010.jpg) 367 | 368 | ### 功能测试 369 | 370 | - 连通性测试 371 | 372 | 在不开启防火墙的情况下,在外网主机上开启apache服务,此时,在浏览器中输入外网IP(访问其80端口),可以访问其主页;ping外网主机IP也能ping通 373 | 374 | ![连通性测试](images/clip_image002-164140716212611.jpg) 375 | 376 | - 添加规则 377 | 378 | 使用-a命令为防火墙添加以下两条规则: 379 | 380 | | **src_ip** | **dst_ip** | **src_mark** | **dst_mark** | **src_port** | **dst_port** | **ptc** | **act** | **log** | 381 | | -------------- | -------------- | ------------ | ------------ | ------------ | ------------ | ------- | ------- | ------- | 382 | | 192.168.11.129 | 192.168.23.129 | 32 | 32 | -1 | 80 | TCP | 1 | 1 | 383 | | 192.168.11.129 | 192.168.23.129 | 32 | 32 | -1 | -1 | ANY | 0 | 1 | 384 | 385 | ![新增规则](images/clip_image002-164140717434112.jpg) 386 | 387 | 上述两条规则中,规则1允许内网主机使用TCP协议访问外网主机的80端口,规则2禁止内网主机对外网主机的任何访问。将规则添加之后可以看见本地数据库中已经保存了这两条规则。 388 | 389 | - 规则测试 390 | 391 | 添加上述两台规则之后,使用 -c命令将规则commit到内核模块中。此时,内网主机依旧能访问外网主机的网页,但已经无法ping通外网主机。 392 | 393 | ![测试结果](images/clip_image002-164140719545413.jpg) 394 | 395 | 之后,将规则1删除,再次进行上述测试。可以发现此时内网主机既无法访问外网主机的HTTP服务,也无法ping通外网主机 396 | 397 | ![测试结果](images/clip_image004.jpg) 398 | 399 | - 信息获取 400 | 401 | 使用-p参数获取当前的日志信息与连接信息,可以发现系统能够正确记录连接与日志。 402 | 403 | ![信息获取](images/clip_image002-164140721398714.jpg) 404 | 405 | ## 0x07 心得体会及意见建议 406 | 407 | 本次实验是大学阶段最后一个课程设计,要求实现了一个状态检测防火墙,实现了基于连接的检测,不需要对每个数据包进行规则检查,而是一个连接的后续数据包(通常是大量的数据包)通过散列(hash) 算法,直接进行状态检查,从而使得性能得到了较大。 408 | 409 | 在实验过程中,遇到的第一个问题是对Netfilter框架没有了解,不知道从何下手,还好在查阅了大量资料之后掌握了Netfilter和钩子函数的基本用法。 410 | 411 | 由于系统同时包含内核模块与用户模块,虽然之前接触过Linux系统编程,但由于间隔时间过长,再加上内核编程与普通应用程序开发确实有比较大的差别,在开始的时候走了不少弯路。不过在编写字符设备驱动时,由于以前有相关方面的经验,这一部分的代码写的比较顺利。 412 | 413 | 另外一个特点是在内核模块中维护连接表。内核模块中日志与规则的管理都能简单地用数组实现,但是由于连接信息需要频繁地动态修改,不能采用之前地数据结构来存储连接信息。在和同学进行细致的讨论之后,决定采用链表与HASH表结合的方法,将每个连接作为一个链表结点,同时将连接的剩余时间存放在HASH表中,这样既保证了Hash的快速访问,又支持超时连接的删除。 414 | 415 | 对于连接的时间控制上也遇到了一些小麻烦。由于对Linux内核定时器机制不了解,不知道执行完定时中断函数后操作系统会将定时处理函数从事务处理链表中摘下,查阅相关资料后修改了中断函数的代码,在执行完中断逻辑后再次将定时器加到事务处理链表中。 416 | 417 | 由于内核编程常常会涉及到整个操作系统,特别是内核态与用户态的交互,实验过程中每一个新功能的尝试都可能导致虚拟的奔溃死机。而且内核编程不像普通应用程序开发一样有强大的现代工具作为辅助,常常是几个小时都找不到问题所在,这点我在编写内核模块之后编写用户模块有深刻的体会。这也告诉我们,尽管现在编程工具越来越强大,但只有加强自身能力,编写规范的代码,才能在任何情况下写出一个满意的程序,成为一名优秀的计算机工作者。 418 | -------------------------------------------------------------------------------- /参考资料/RJFireWall_README.md: -------------------------------------------------------------------------------- 1 | # RJ FireWall 2 | 3 | 一个基于Netfilter、Netlink的Linux传输层状态检测防火墙,核心代码不到2000行,使用红黑树实现状态检测,内核模块代码通过读写锁几乎全面实现并发安全。 4 | 5 | 仅用于学习与交流,**一定程度上**可以放心使用。 6 | 7 | 支持的功能: 8 | - [x] 按源目的IP、端口、协议过滤报文 9 | - [x] 掩码 10 | - [x] 并发安全 11 | - [x] 通过命令行应用新增、删除、查看过滤规则,更改默认动作 12 | - [x] 记录报文过滤日志及通过命令行应用查看 13 | - [x] 连接状态检测与记录 14 | - [x] 通过命令行应用查看已建立的所有连接 15 | - [x] NAT 16 | - [x] 配置NAT规则 17 | - [ ] ~~图形化界面 防火墙还写啥GUI~~ 18 | 19 | # 安装 20 | 21 | ### 环境 22 | 23 | 我所采用的环境为Ubuntu 16.04,Linux内核版本4.15,所有功能测试正常。 24 | 25 | **一般而言**,所有Linux内核版本 > 4.9的系统皆可使用。 26 | 27 | ### 从源码安装 28 | 29 | 安装时需要gcc以及make包,若未安装,请预先安装: 30 | ```bash 31 | sudo apt install gcc make 32 | ``` 33 | 34 | 首先,下载本项目源码至任意目录: 35 | ```bash 36 | unzip RJFireWall.zip 37 | 38 | cd RJFireWall 39 | ``` 40 | 41 | 随后,**编译源码**: 42 | ```bash 43 | sudo make 44 | ``` 45 | 46 | 最后,**安装**: 47 | ```bash 48 | sudo make install 49 | ``` 50 | 51 | # 使用 52 | 53 | 在安装时,内核模块已经加载至Linux内核中,此时,只需使用上层应用uapp来对防火墙进行控制即可。 54 | 55 | 新增一条过滤规则: 56 | ```bash 57 | ./uapp rule add 58 | ``` 59 | 随后依据命令行提示设定规则即可。 60 | 61 | > SSD PS:没有规则时,`add rule after`可任意输入。 62 | 63 | 删除一条过滤规则: 64 | ```bash 65 | ./uapp rule del 所需删除规则的名称 66 | ``` 67 | 68 | 设置默认动作为Drop(防火墙初始时默认动作为Accept): 69 | ```bash 70 | ./uapp rule default drop 71 | ``` 72 | 73 | 展示已有规则: 74 | ```bash 75 | ./uapp ls rule 76 | ``` 77 | 78 | 展示所有过滤日志: 79 | ```bash 80 | ./uapp ls log 81 | ``` 82 | 83 | 展示最后100条过滤日志: 84 | ```bash 85 | ./uapp ls log 100 86 | ``` 87 | 88 | 展示当前已有连接: 89 | ```bash 90 | ./uapp ls connect 91 | ``` 92 | 93 | 新增一条NAT规则: 94 | ```bash 95 | ./uapp nat add 96 | ``` 97 | 随后依据命令行提示设定规则即可。 98 | 99 | 删除一条NAT规则: 100 | ```bash 101 | ./uapp nat del 所需删除NAT规则的序号 102 | ``` 103 | 104 | 展示已有NAT规则: 105 | ```bash 106 | ./uapp ls nat 107 | ``` -------------------------------------------------------------------------------- /参考资料/fix_Netfilter-Firewall_README.md: -------------------------------------------------------------------------------- 1 | # Linux下状态检测防火墙的设计与实现 2 | 3 | ## 0x00 简介 4 | 5 | 本实验为2022年华中科技大学网络空间安全学院《网络安全课程设计》的实验,要求实现一个带NAT功能的状态检测防火墙。 6 | 7 | 改写了原来的README的部分内容,但没改完。 8 | 9 | ## 0x01 实验目的和要求 10 | 11 | **实验目的** 12 | 13 | - 结合理论课程学习,深入理解计算机网络安全的基本 原理与协议,巩固计算机网络安全基本理论知识 14 | 15 | - 熟练掌握计算机网络编程方法,拓展学生的应用能力 16 | 17 | - 加强对网络协议栈的理解 18 | 19 | - 提高分析、设计软件系统以及编写文档的能力 20 | 21 | - 培养团队合作能力。 22 | 23 | **实验要求** 24 | 25 | - 正确理解题意 26 | 27 | - 具有良好的编程规范和适当的注释 28 | 29 | - 有详细的文档,文档中应包括设计题目涉及的基础知识、 设计思路、程序流程图、程序清单、开发中遇到的问题及解决方法、设计中待解决的问题及改进方向 30 | 31 | **任务要求:** 32 | 33 | 1. 系统运行 34 | - 系统启动以后插入模块,防火墙以内核模块方式运行 35 | - 应用程序读取配置,向内核写入规则,报文到达,按照规则进行处理 36 | 2. 界面 37 | - 采用图形或者命令行方式进行规则配置,界面友好 38 | 3. 功能要求 39 | - 能对TCP、UDP、ICMP协议的报文进行状态分析和过滤 40 | - 每一条过滤规则至少包含:报文的源IP(带掩码的网络地址)、目的IP(带掩码的网络地址)、源端口、目的端口、 协议、动作(禁止/允许),是否记录日志 41 | - 过滤规则可以进行添加、删除、保存,配置的规则能立即生效 42 | - 过滤日志可以查看 43 | - 具有NAT功能,转换地址分按接口地址转换和指定地址转 换(能实现源或者目的地址转换的任一种即可) 44 | - 能查看所有连接状态信息 45 | 4. 测试 46 | 47 | - 测试系统是否符合设计要求 48 | - 系统运行稳定 49 | - 性能分析 50 | 51 | ## 0x02 实验原理 52 | 53 | ### 状态检测技术 54 | 55 | 状态检测技术采用的是一种基于连接的状态检测机制,将属于同一连接的所有包作为一个整体的数据流看待,构成连接状态表,通过规则表与状态表的共同配合,对表中的各个连接状态因素加以识别。 56 | 57 | 这些状态信息可由状态检测表存储。状态检测表包括所有通过防火墙的连接,并维护所有连接。每条连接的信息包括源地址、目的地址、协议类型、协议相关信息(如TCP/UDP协议的端口、ICMP协议的ID号)、连接状态(如TCP连接状态)和超时时间(若进行NAT转换,还需记录转换前后地址端口信息)等,防火墙把这些信息叫做状态。通过状态检测,可实现比简单包过滤防火墙更大的安全性及更高的性能。 58 | 59 | 对于TCP连接,一次连接以3次握手开始,以4次挥手或Reset报文结束。由于TCP协议是询问-应答类型的可靠数据传输协议,故TCP连接存在状态,对于同一连接的报文,只需对其握手报文进行规则匹配即可,若允许通过,则同一TCP连接的所有报文均可通过,无需遍历规则表。因此其处理流程如下图2-1所示。 60 | 61 | ![TCP连接处理流程](imgs/0001_TCP处理流程.png) 62 | 63 | 对于非TCP连接,如UDP、ICMP,防火墙为其建立虚拟连接: 64 | 65 | UDP:一方发出UDP包后,如DNS请求,防火墙会将从目的地址和端口返回的、到达源地址源端口的包作为状态相关的包而允许通过。 66 | 67 | ICMP:信息查询报文,如ping(echo)包,其状态相关包就是来自目的地址的echo reply包,而没有与echo包对应的echo reply包则认为是状态非法的。 68 | 69 | 上述这些连接在防火墙的状态检测表中的超时时间比较短。 70 | 71 | 在实现状态检测表时,常采用非线性数据结构(如hash表、树等),并直接在内核执行匹配,采用硬件支持,速度快。 72 | 73 | ### Linux Netfilter架构 74 | 75 | Netfilter 是 Linux 内核中进行数据包过滤,连接跟踪(Connect Track),网络地址转换(NAT)等功能的主要实现框架;该框架在网络协议栈处理数据包的关键流程中定义了一系列钩子点(Hook 点),并在这些钩子点中注册一系列函数对数据包进行处理。这些注册在钩子点的函数即为设置在网络协议栈内的数据包通行策略,也就意味着,这些函数可以决定内核是接受还是丢弃某个数据包,换句话说,这些函数的处理结果决定了这些网络数据包的“命运”。 76 | 77 | Netfilter框架在网络协议各层的各个hook点如下图所示。 78 | 79 | ![Netfilter框架hook点](images/clip_image002-16414060761671.jpg) 80 | 81 | ### Linux字符设备驱动 82 | 83 | 字符设备:是指只能一个字节一个字节进行读写操作的设备,不能随机读取设备中的某一数据、读取数据要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED等。 84 | 85 | 字符设备可以通过文件节点来访问,比如/dev/tty1和/dev/lp0等。这些设备文件和普通文件之间的唯一差别在于对普通文件的访问可以前后移动访问位置,而大多数字符设备是一个只能顺序访问的数据通道。然而,也存在具有数据区特性的字符设备,访问它们时可前后移动访问位置。例如framebuffer就是这样的一个设备,app可以用mmap或lseek访问抓取的整个图像。 86 | 87 | 如图,在Linux内核代码中: 88 | 89 | - 使用struct cdev结构体来抽象一个字符设备; 90 | - 通过一个dev_t类型的设备号(分为主(major)、次设备号(minor))一确定字符设备唯一性; 91 | - 通过struct file_operations类型的操作方法集来定义字符设备提供个VFS的接口函数。 92 | 93 | ![Linux字符设备驱动](images/clip_image002.png) 94 | 95 | ## 0x03 实验环境和采用的工具 96 | 97 | 操作系统:Ubuntu14.04 98 | 99 | 内核版本:Linux 4.4.0-142-generic 100 | 101 | 编译环境:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) 102 | 103 | ## 0x04 系统设计 104 | 105 | ### 系统静态结构设计 106 | 107 | - 内核模块设计 108 | 109 | 内核模块由访问控制处理逻辑和字符设备驱动构成。访问控制处理逻辑负责对报文进行过滤分析,实现状态监测;字符设备驱动负责内核态与用户态的交互。 110 | 111 | 访问控制处理逻辑维护了规则表、日志表、HASH表与连接链、NAT表。其中,规则表负责记录规则信息,包括七元组(五元组+IP掩码)、动作、以及是否记录日志等;日志表负责记录规则与连接信息;HASH表与连接链共同维护连接信息,包括连接双方的网络信息、剩余超时时间等;NAT表负责维护NAT地址转换。 112 | 113 | hook_in和hook_out两个Hook函数维护了连接链和HASH表。通过查询规则表的访问控制规则决定是否允许报文通过,同时更新连接链和Hash表的状态信息,并且对匹配的规则记录日志。 114 | 115 | 字符设备驱动部分通过对字符设备的操作实现内核模块与用户模块的信息传递。其中,.read操作负责根据操作码,将内核模块所维护的连接信息、日志信息以及NAT信息发送给字符设备供用户模块读取;.write操作负责从字符设备读取操作码,实现规则载入以及调用.read实现信息输出。 116 | 117 | - 用户模块设计 118 | 119 | 用户模块由C++实现,完成了规则的维护(包括存储、读取、新增、删除、以及载入到内核)、连接信息的获取、日志获取。 120 | 121 | 用户模块项目结构如下所示,将内核模块的5个重要数据结构抽象成类。 122 | 123 | 内核模块的规则信息在用户模块抽象成Rule类。主要实现新规则的构造、规则信息的格式转换,以及规则展示。 124 | 125 | 内核模块的日志信息在用户模块抽象成Log类。主要实现五元组信息的记录以及日志信息的展示。 126 | 127 | 内核模块的HASH表与连接链在用户模块共同抽象成Connection类。主要记录了连接状态信息,除五元组外还包含连接剩余时间。 128 | 129 | 内核模块的NAT信息在用户模块抽象成NAT类。主要实现NAT信息的记录、修改与展示。 130 | 131 | 除了规则Rule类之外,剩余三个类都是直接与内核模块进行信息交互,信息流通方向主要是从内核模块到用户模块单向流通。但Rule信息采用了本地存储-远程提交的方法,通过在用户模块建立本地数据库对规则进行增、删、查、改的统一管理,再通过commit操做将整个规则表发送给内核模块,有效的减少了不必要的信息流动,增加了系统工作效率。 132 | 133 | ### 系统核心数据流程 134 | 135 | hook_in与hook_out两个hook函数获取到流量包后,统一交付给check_pkg函数处理。check_pkg函数首先根据流量包的五元组信息检查该流量包是否属于已经建立的连接。若属于,则直接放行,并更新所属连接的存活时间;否则,将根据流量包的类型(TCP/UDP/ICMP/其它)以及规则表来确定是创建新连接还是拒绝流量包通过。 136 | 137 | ## 0x05 系统详细设计 138 | 139 | ### 关键数据结构设计 140 | 141 | - 内核模块数据结构 142 | 143 | 1. 规则表 144 | 145 | 规则表为struct rule结构体的数组,同时通过static int 类型的rule_num变量来维护规则表的大小。 146 | 147 | struct rule结构体定义如下: 148 | 149 | | **类型** | **变量** | **描述** | 150 | | -------- | -------- | ---------------------------- | 151 | | unsigned | src_ip | 源IP | 152 | | unsigned | dst_ip | 目的IP | 153 | | unsigned | src_mask | 源IP掩码 | 154 | | unsigned | dst_mask | 目的IP掩码 | 155 | | int | src_port | 源端口 | 156 | | int | dst_port | 目的端口 | 157 | | int | protocol | 协议号 | 158 | | int | action | 动作(0丢弃,1通过) | 159 | | int | log | 是否记录日志(0忽略,1记录) | 160 | 161 | 2. 日志表 162 | 163 | 日志表为struct log结构体的数组,同时通过static int 类型的log_num变量来维护日志表的大小。 164 | 165 | struct log结构体定义如下: 166 | 167 | | **类型** | **变量** | **描述** | 168 | | -------- | -------- | -------------------- | 169 | | unsigned | src_ip | 源IP | 170 | | unsigned | dst_ip | 目的IP | 171 | | int | src_port | 源端口 | 172 | | int | dst_port | 目的端口 | 173 | | int | protocol | 协议号 | 174 | | int | action | 动作(0丢弃,1通过) | 175 | 176 | 3. 连接链与HASH表 177 | 178 | 连接链为struct connection结构体的链表,表头结点为空结点conHead。每个结点包括该连接的基本五元组信息以及一个用于定位到HASH表的索引字段。 179 | 180 | struct connection结构体定义如下: 181 | 182 | | **类型** | **变量** | **描述** | 183 | | ------------------- | -------- | -------------- | 184 | | unsigned | src_ip | 源IP | 185 | | unsigned | dst_ip | 目的IP | 186 | | int | src_port | 源端口 | 187 | | int | dst_port | 目的端口 | 188 | | int | protocol | 协议号 | 189 | | int | index | HASH索引 | 190 | | struct connection\* | next | 下一个连接结点 | 191 | 192 | HASH表为一个字符数组,记录了连接链中每个连接的剩余存活时间。为了实现HASH表的功能,设计了一个HASH函数,将每个连接的五元组信息作为HASH函数的关键字映射到HASH表中。 193 | 194 | 连接链与HASH表的映射关系如下图所示: 195 | 196 | ![连接链到HASH表的映射](images/clip_image002-16414063769822.jpg) 197 | 198 | - 用户模块数据结构 199 | 200 | 1. Rule类 201 | 202 | Rule类对应内核模块的规则表,提供对规则信息提取与操做多个接口。 203 | 204 | | 变量 | 类型 | 描述 | 205 | | -------- | -------- | ------------ | 206 | | src_ip | unsigned | 源IP | 207 | | dst_ip | unsigned | 目的IP | 208 | | src_mark | unsigned | 源IP掩码 | 209 | | dst_mark | unsigned | 目的IP掩码 | 210 | | src_port | int | 源端口 | 211 | | dst_port | int | 目的端口 | 212 | | protocol | int | 协议号 | 213 | | action | int | 是否通过 | 214 | | log | int | 是否记录日志 | 215 | 216 | | 方法 | 返回值 | 描述 | 217 | | ---------------------------- | -------- | --------------------------------------------------------- | 218 | | Rule() | 构造函数 | 创建一个新的Rule对象 | 219 | | Rule(char* data, int offset) | 构造函数 | 创建一个新的Rule对象(从data指向地址+offset偏移处获取数据) | 220 | | srcIP(std::string ip_str) | void | 将对象的src_ip修改为ip_str所指向的IP | 221 | | dstIP(std::string ip_str) | void | 将对象的dst_ip修改为ip_str所指向的IP | 222 | | srcMask(std::string ip_str) | void | 将对象的src_mask修改为ip_str所指向的掩码 | 223 | | dstMask(std::string ip_str) | void | 将对象的dst_mask修改为ip_str所指向的掩码 | 224 | | print() | void | 将对象按标准格式打印 | 225 | 226 | 2. Log类 227 | 228 | Log类对应内核模块的日志表,提供对日志信息提取与操做多个接口。 229 | 230 | | 变量 | 类型 | 描述 | 231 | | -------- | -------- | -------- | 232 | | src_ip | unsigned | 源IP | 233 | | dst_ip | unsigned | 目的IP | 234 | | src_port | int | 源端口 | 235 | | dst_port | int | 目的端口 | 236 | | protocol | int | 协议号 | 237 | | action | int | 是否通过 | 238 | 239 | | 方法 | 返回值 | 描述 | 240 | | --------------------------- | -------- | -------------------------------------------------------- | 241 | | Log(char *data, int offset) | 构造函数 | 创建一个新的Log对象(从data指向地址+offset偏移处获取数据) | 242 | | print() | void | 将对象按标准格式打印 | 243 | 244 | 3. Connection类 245 | 246 | Connection类对应内核模块的连接表,提供对连接信息提取与操做多个接口。 247 | 248 | | 变量 | 类型 | 描述 | 249 | | -------- | -------- | ------------ | 250 | | src_ip | unsigned | 源IP | 251 | | dst_ip | unsigned | 目的IP | 252 | | src_port | int | 源端口 | 253 | | dst_port | int | 目的端口 | 254 | | protocol | int | 协议号 | 255 | | time | int | 剩余存活时间 | 256 | 257 | | 方法 | 返回值 | 描述 | 258 | | ---------------------------------- | -------- | ------------------------------------------------------------ | 259 | | Connection(char *data, int offset) | 构造函数 | 创建一个新的Connection对象(从data指向地址+offset偏移处获取数据) | 260 | | print() | void | 将对象按标准格式打印 | 261 | 262 | 4. 其它功能函数 263 | 264 | | 返回值 | 函数 | 描述 | 265 | | -------- | --------------------------------- | --------------------------------------------------------- | 266 | | unsigned | strToIp(std::string ip_str) | 将string类型的ip转换为unsigned类型 | 267 | | string | ipToStr(unsigned ip) | 将unsigned类型的ip转换为string类型 | 268 | | int | strToPtc(std::string ptc_str) | 将string类型的ptc(协议名)转换为int类型(协议号) | 269 | | string | ptcToStr(int ptc) | 将int类型的ptc(协议号)转换为string类型(协议名) | 270 | | unsigned | byteToInt(char *byte, int offset) | 将byte指向地址+offset偏移出的字节数据转换为int类型 | 271 | | string | toAct(int x) | 将int类型的action转换为string类型(0-> “Deny”, 1->“Accept” | 272 | | string | toLog(int x) | 将int类型的log转换为string类型(1->“Loged”, 0->“Unloged” | 273 | 274 | ### 关键模块流程设计 275 | 276 | - 内核模块流程 277 | 278 | 1. 报文过滤 279 | 280 | 报文过滤是本次防火墙实验主要需要实现的功能。内核模块中,对于NF_INET_PRE_ROUTING和NF_INET_POST_ROUTING两个hook点分别设置了hook_in与hook_out函数。两个hook函数实际上均是调用名为check_pkg的函数对经过hook点的报文进行判断是否允许通过。 281 | 282 | 在check_pkg函数中,首先提取出报文的五元组信息并判断是否属于已有连接的报文(详见下一节:**状态检测**)。如果属于已有连接则直接放行,否则将报文的五元组信息与规则表中存储的规则逐条匹配来决定能否通过。如果是允许通过的报文,则在连接链与HASH表中创建一个新的连接信息,以便后续的检测使用。 283 | 284 | 报文过滤流程图如下所示: 285 | 286 | ![报文过滤流程](images/clip_image002-16414068716543.jpg) 287 | 288 | 2. 状态检测 289 | 290 | 与普通的包过滤防火墙不同,状态检测防火墙主要基于连接来判断是否允许一个报文通过。 291 | 292 | 为了建立报文与连接的映射,将报文的五元组(源IP,目的IP,源端口,目标端口,协议号)一起作为HASH函数的关键字进行哈希,计算出的HASH值即当前报文对应连接在HASH表中的位置。 293 | 294 | HASH表记录了当前连接的剩余存活时间。如果剩余存活时间为0,说明当前报文未创建连接或连接已超时,内核模块将根据规则表来决定是否为其创建连接;如果剩余时间不为0,说明已经为当前报文创建连接,直接将报文放行并更新剩余时间。 295 | 296 | HASH函数如下所示: 297 | 298 | ![HASH函数](images/clip_image002-16414068912734.jpg) 299 | 300 | 3. 时间控制 301 | 302 | 为了防止资源被占用,需要将连接链中长期没有数据流通的连接删除。Linux内核中提供了一个定时器的功能用于处理周期性工作,我们将使用这个功能来实现时间控制功能。 303 | 304 | 创建一个struct timer_list定时器并绑定到time_out函数。当定时器发生超时时,将会调用time_out函数。 305 | 306 | 在time_out函数中,我们将遍历整条连接链,将所有连接的剩余时间减1。当某个连接的剩余时间被减为0时,表明该链接已经超时,此时将会将该连接结点释放并继续遍历。 307 | 308 | 定时器的超时时间为1秒;连接被创建时初始剩余时间为60。因此当某个连接超过1分钟没有进行数据传输时即会被断开。 309 | 310 | 4. 字符设备驱动 311 | 312 | 为了让内核模块与用户模块进行数据的交互,系统采用字符设备的方法实现了内核模块和用户模块的信息交互。 313 | 314 | 为了区分传递信息的流向与内容,内核模块维护了一个全局操作符变量,并定义了3个操作符: 315 | 316 | | 操作符 | 值 | 含义 | 317 | | -------------- | ---- | ---------------------------- | 318 | | OP_WRITE_RULR | 0 | 内核模块从用户模块取规则信息 | 319 | | OP_GET_CONNECT | 1 | 用户模块从内核模块取连接信息 | 320 | | OP_GET_LOG | 2 | 用户模块从内核模块取日志信息 | 321 | 322 | 每次进行数据交互时,规定总是首先由用户模块向内核模块写入数据,且写入数据的最后一个字节为上述操作符之一。 323 | 324 | 当用户模块写入数据时,判断其操作符。操作符为OP_WRITE_RULR时,内核模块将读取用户模块写入的所有数据,并将数据逐条存入内核模块的规则表中;当操作符为OP_GET_CONNECT或OP_GET_LOG时,内核模块将更新其维护的全局操作符为对应的值; 325 | 326 | 当用户模块读取数据时,由于在上一步的写入操作符过程中更新了其全局操作符,内核模块将根据全局操作符的值来决定是将日志信息还是连接信息写入字符设备,再由用户模块读取。 327 | 328 | 数据传输流程如下图所示: 329 | 330 | ![用户模块与内核模块的数据交换](images/clip_image002-16414069119185.jpg) 331 | 332 | - 用户模块流程 333 | 334 | 用户模块用C++实现。 335 | 336 | ​ 程序运行时,将根据其传入的参数决定功能。具体功能如下图所示: 337 | 338 | ​ ![程序使用方法](images/clip_image002-16414069356946.jpg) 339 | 340 | 1. 规则的增删查改 341 | 342 | 用户模块最重要的功能在于规则的维护,包括输出规则、新建规则与删除规则。 343 | 344 | 不同于日志信息与连接信息主要由内核模块维护,规则信息采取了“本地存储+远程推送”的模式,即:规则的存储、新增、查询、删除都是在本地数据库中进行操作,内核模块只能单向地接受来自用户模块所提交的规则,而不能将规则信息输出到用户模块。这样既能保证每次机器重启时都能保存之前所创建的规则表,又减少了内核模块与用户模块之间频繁的数据传输,增加了系统执行效率。 345 | 346 | 对于新建规则,程序将根据用户输入信息创建一个新的Rule类型的对象,并通过文件读写操作将其保存在本地数据库;对于删除规则,程序将根据索引值在本地数据库中删除对应规则的信息;对于查询规则,程序将把本地数据库中所存储的规则添加到用户模块所维护的规则表中。 347 | 348 | 当本地规则维护完毕之后,用户可使用 `-c/--commit` 参数将规则提交到内核模块。此时,用户模块首先将本地数据库中的规则信息全部加载到用户模块所维护的规则表中。之后用户模块向内核模块所创建的字符设备输入OP_WRITE_RULR操作符,并将用户模块所存储的规则表全部输入到字符设备;内核模块收到OP_WRITE_RULR操作符时,将会开始接受字符设备中的规则表,实现了规则表从用户模块向规则模块的流通。 349 | 350 | ![系统之间信息传输](images/clip_image002-16414069505887.jpg) 351 | 352 | 2. 日志信息打印 353 | 354 | 当使用 `-p l/log` 命令时,程序将打印当前存储的日志信息。 355 | 356 | 当使用 `-p l/log` 命令时,用户模块将会向内核模块所创建的字符设备输入OP_GET_LOG操作符。内核模块收到操作符时,会把其所维护的日志信息输出到字符设备中,用户模块再读取字符设备中所获得的日志信息加载到其所维护的日志表中并打印到屏幕上。 357 | 358 | ![日志信息打印](images/clip_image002-16414069922098.jpg) 359 | 360 | 3. 连接信息打印 361 | 362 | 当使用 `-p c/connection` 命令时,程序将打印当前存储的日志信息。 363 | 364 | 当使用 `-p c/connection` 命令时,用户模块将会向内核模块所创建的字符设备输入OP_GET_CONNECT 操作符。内核模块收到操作符时,会把其所维护的连接信息输出到字符设备中,用户模块再读取字符设备中所获得的连接信息加载到其所维护的连接表中并打印到屏幕上。 365 | 366 | ![连接信息打印](images/clip_image002-16414070055289.jpg) 367 | 368 | ## 0x06 系统测试 369 | 370 | ### 测试环境 371 | 372 | 内网网段为192.168.11.0/24,外网网段为192.168.23.0/24。部署三台虚拟机,分别模拟内网主机、外网主机与防火墙。其中内网主机IP为192.168.11.129,外网主机IP为192.168.23.129,防火墙同时连接内外网负责转发,IP为192.168.11.128(内网)、192.168.23.128(外网) 373 | 374 | ![实验拓扑环境](images/clip_image002-164140710634010.jpg) 375 | 376 | ### 功能测试 377 | 378 | - 连通性测试 379 | 380 | 在不开启防火墙的情况下,在外网主机上开启apache服务,此时,在浏览器中输入外网IP(访问其80端口),可以访问其主页;ping外网主机IP也能ping通 381 | 382 | ![连通性测试](images/clip_image002-164140716212611.jpg) 383 | 384 | - 添加规则 385 | 386 | 使用-a命令为防火墙添加以下两条规则: 387 | 388 | | **src_ip** | **dst_ip** | **src_mark** | **dst_mark** | **src_port** | **dst_port** | **ptc** | **act** | **log** | 389 | | -------------- | -------------- | ------------ | ------------ | ------------ | ------------ | ------- | ------- | ------- | 390 | | 192.168.11.129 | 192.168.23.129 | 32 | 32 | -1 | 80 | TCP | 1 | 1 | 391 | | 192.168.11.129 | 192.168.23.129 | 32 | 32 | -1 | -1 | ANY | 0 | 1 | 392 | 393 | ![新增规则](images/clip_image002-164140717434112.jpg) 394 | 395 | 上述两条规则中,规则1允许内网主机使用TCP协议访问外网主机的80端口,规则2禁止内网主机对外网主机的任何访问。将规则添加之后可以看见本地数据库中已经保存了这两条规则。 396 | 397 | - 规则测试 398 | 399 | 添加上述两台规则之后,使用 -c命令将规则commit到内核模块中。此时,内网主机依旧能访问外网主机的网页,但已经无法ping通外网主机。 400 | 401 | ![测试结果](images/clip_image002-164140719545413.jpg) 402 | 403 | 之后,将规则1删除,再次进行上述测试。可以发现此时内网主机既无法访问外网主机的HTTP服务,也无法ping通外网主机 404 | 405 | ![测试结果](images/clip_image004.jpg) 406 | 407 | - 信息获取 408 | 409 | 使用-p参数获取当前的日志信息与连接信息,可以发现系统能够正确记录连接与日志。 410 | 411 | ![信息获取](images/clip_image002-164140721398714.jpg) 412 | 413 | ## 0x07 心得体会及意见建议 414 | 415 | 本次实验是大学阶段最后一个课程设计,要求实现了一个状态检测防火墙,实现了基于连接的检测,不需要对每个数据包进行规则检查,而是一个连接的后续数据包(通常是大量的数据包)通过散列(hash) 算法,直接进行状态检查,从而使得性能得到了较大。 416 | 417 | 在实验过程中,遇到的第一个问题是对Netfilter框架没有了解,不知道从何下手,还好在查阅了大量资料之后掌握了Netfilter和钩子函数的基本用法。 418 | 419 | 由于系统同时包含内核模块与用户模块,虽然之前接触过Linux系统编程,但由于间隔时间过长,再加上内核编程与普通应用程序开发确实有比较大的差别,在开始的时候走了不少弯路。不过在编写字符设备驱动时,由于以前有相关方面的经验,这一部分的代码写的比较顺利。 420 | 421 | 另外一个特点是在内核模块中维护连接表。内核模块中日志与规则的管理都能简单地用数组实现,但是由于连接信息需要频繁地动态修改,不能采用之前地数据结构来存储连接信息。在和同学进行细致的讨论之后,决定采用链表与HASH表结合的方法,将每个连接作为一个链表结点,同时将连接的剩余时间存放在HASH表中,这样既保证了Hash的快速访问,又支持超时连接的删除。 422 | 423 | 对于连接的时间控制上也遇到了一些小麻烦。由于对Linux内核定时器机制不了解,不知道执行完定时中断函数后操作系统会将定时处理函数从事务处理链表中摘下,查阅相关资料后修改了中断函数的代码,在执行完中断逻辑后再次将定时器加到事务处理链表中。 424 | 425 | 由于内核编程常常会涉及到整个操作系统,特别是内核态与用户态的交互,实验过程中每一个新功能的尝试都可能导致虚拟的奔溃死机。而且内核编程不像普通应用程序开发一样有强大的现代工具作为辅助,常常是几个小时都找不到问题所在,这点我在编写内核模块之后编写用户模块有深刻的体会。这也告诉我们,尽管现在编程工具越来越强大,但只有加强自身能力,编写规范的代码,才能在任何情况下写出一个满意的程序,成为一名优秀的计算机工作者。 426 | -------------------------------------------------------------------------------- /参考资料/hping3功能以及参数介绍_一只青木呀-CSDN博客_hping3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/hping3功能以及参数介绍_一只青木呀-CSDN博客_hping3.pdf -------------------------------------------------------------------------------- /参考资料/images/Snipaste_2022-10-31_15-33-43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/Snipaste_2022-10-31_15-33-43.png -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414060761671.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414060761671.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414063769822.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414063769822.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414068716543.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414068716543.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414068912734.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414068912734.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414069119185.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414069119185.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414069356946.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414069356946.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414069505887.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414069505887.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414069922098.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414069922098.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-16414070055289.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-16414070055289.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-164140710634010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-164140710634010.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-164140716212611.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-164140716212611.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-164140717434112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-164140717434112.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-164140719545413.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-164140719545413.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002-164140721398714.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002-164140721398714.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002.jpg -------------------------------------------------------------------------------- /参考资料/images/clip_image002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image002.png -------------------------------------------------------------------------------- /参考资料/images/clip_image004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/images/clip_image004.jpg -------------------------------------------------------------------------------- /参考资料/imgs/0001_tcp处理流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/imgs/0001_tcp处理流程.png -------------------------------------------------------------------------------- /参考资料/imgs/0002_Netfilter框架.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/imgs/0002_Netfilter框架.png -------------------------------------------------------------------------------- /参考资料/imgs/0003_内核交互.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/imgs/0003_内核交互.png -------------------------------------------------------------------------------- /参考资料/别人的代码_nltest/Makefile: -------------------------------------------------------------------------------- 1 | MODULE_NAME := nltest_kmod 2 | 3 | obj-m := $(MODULE_NAME).o 4 | 5 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 6 | 7 | PWD := $(shell pwd) 8 | 9 | 10 | all: mod app 11 | 12 | mod: 13 | $(MAKE) -C $(KERNELDIR) M=$(PWD) 14 | 15 | app: 16 | gcc -o nltest_uapp nltest_uapp.c 17 | 18 | clean: 19 | rm -fr *.ko *.o *.cmd nltest_uapp $(MODULE_NAME).mod.c modules.order Module.symvers 20 | 21 | -------------------------------------------------------------------------------- /参考资料/别人的代码_nltest/nltest_kmod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define NETLINK_TEST 17 9 | 10 | static struct sock *nlsk = NULL; 11 | 12 | int nltest_ksend(char *info, int pid) 13 | { 14 | char reply[256]; 15 | int rlen; 16 | struct sk_buff *skb; 17 | struct nlmsghdr *nlh; 18 | int retval; 19 | 20 | sprintf(reply, "NLTEST Reply for '%s'", info); 21 | rlen = strlen(reply) + 1; 22 | 23 | skb = nlmsg_new(rlen, GFP_ATOMIC); 24 | if (skb == NULL) { 25 | printk("alloc reply nlmsg skb failed!\n"); 26 | return -1; 27 | } 28 | 29 | nlh = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(rlen) - NLMSG_HDRLEN, 0); 30 | memcpy(NLMSG_DATA(nlh), reply, rlen); 31 | printk("[kernel space] nlmsglen = %d\n", nlh->nlmsg_len); 32 | 33 | //NETLINK_CB(skb).pid = 0; 34 | NETLINK_CB(skb).dst_group = 0; 35 | 36 | printk("[kernel space] skb->data send to user: '%s'\n", (char *) NLMSG_DATA(nlh)); 37 | 38 | retval = netlink_unicast(nlsk, skb, pid, MSG_DONTWAIT); 39 | printk("[kernel space] netlink_unicast return: %d\n", retval); 40 | return 0; 41 | } 42 | 43 | void nltest_krecv(struct sk_buff *skb) 44 | { 45 | struct nlmsghdr *nlh = NULL; 46 | char *data; 47 | int pid; 48 | 49 | nlh = nlmsg_hdr(skb); 50 | if ((nlh->nlmsg_len < NLMSG_HDRLEN) || (skb->len < nlh->nlmsg_len)) { 51 | printk("Illegal netlink packet!\n"); 52 | return; 53 | } 54 | 55 | data = (char *) NLMSG_DATA(nlh); 56 | printk("[kernel space] data receive from user: '%s'\n", data); 57 | pid = nlh->nlmsg_pid; 58 | printk("[kernel space] user_pid = %d\n", pid); 59 | nltest_ksend(data, pid); 60 | } 61 | 62 | struct netlink_kernel_cfg nltest_cfg = { 63 | 0, //groups 64 | 0, //flags 65 | nltest_krecv, //input 66 | NULL, //cb_mutex 67 | NULL, //bind 68 | NULL, //unbind 69 | NULL, //compare 70 | }; 71 | 72 | int __init nltest_init(void) 73 | { 74 | printk("Netlink test module initializing...\n"); 75 | nlsk = netlink_kernel_create(&init_net, NETLINK_TEST, &nltest_cfg); 76 | if (!nlsk) { 77 | printk("can not create a netlink socket\n"); 78 | return -1; 79 | } 80 | printk("netlink_kernel_create() success, nlsk = %p\n", nlsk); 81 | return 0; 82 | } 83 | 84 | void __exit nltest_exit(void) 85 | { 86 | sock_release(nlsk->sk_socket); 87 | printk("Netlink test module exit!\n"); 88 | } 89 | 90 | module_init(nltest_init); 91 | module_exit(nltest_exit); 92 | MODULE_LICENSE("GPL"); 93 | -------------------------------------------------------------------------------- /参考资料/别人的代码_nltest/nltest_uapp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define NETLINK_TEST 17 10 | #define MSG_LEN 256 11 | 12 | char *default_data = "Netlink Test Default Data"; 13 | 14 | struct msg_to_kernel { 15 | struct nlmsghdr hdr; 16 | char data[MSG_LEN]; 17 | }; 18 | 19 | struct u_packet_info { 20 | struct nlmsghdr hdr; 21 | char msg[MSG_LEN]; 22 | }; 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | char *data; 27 | int dlen; 28 | struct sockaddr_nl local; 29 | struct sockaddr_nl kpeer; 30 | int skfd, ret, kpeerlen = sizeof(struct sockaddr_nl); 31 | struct nlmsghdr *message; 32 | struct u_packet_info info; 33 | char *retval; 34 | 35 | if (argc == 2) { 36 | data = argv[1]; 37 | } else { 38 | data = default_data; 39 | } 40 | dlen = strlen(data) + 1; 41 | 42 | skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST); 43 | if (skfd < 0) { 44 | printf("can not create a netlink socket\n"); 45 | return -1; 46 | } 47 | 48 | memset(&local, 0, sizeof(local)); 49 | local.nl_family = AF_NETLINK; 50 | local.nl_pid = getpid(); 51 | local.nl_groups = 0; 52 | if (bind(skfd, (struct sockaddr *) &local, sizeof(local)) != 0) { 53 | printf("bind() error\n"); 54 | return -1; 55 | } 56 | memset(&kpeer, 0, sizeof(kpeer)); 57 | kpeer.nl_family = AF_NETLINK; 58 | kpeer.nl_pid = 0; 59 | kpeer.nl_groups = 0; 60 | 61 | message = (struct nlmsghdr *) malloc(sizeof(struct msg_to_kernel)); 62 | if (message == NULL) { 63 | printf("malloc() error\n"); 64 | return -1; 65 | } 66 | 67 | memset(message, '\0', sizeof(struct nlmsghdr)); 68 | message->nlmsg_len = NLMSG_SPACE(dlen); 69 | message->nlmsg_flags = 0; 70 | message->nlmsg_type = 0; 71 | message->nlmsg_seq = 0; 72 | message->nlmsg_pid = local.nl_pid; 73 | 74 | retval = memcpy(NLMSG_DATA(message), data, strlen(data)); 75 | 76 | printf("message sendto kernel, content: '%s', len: %d\n", (char *) NLMSG_DATA(message), message->nlmsg_len); 77 | ret = sendto(skfd, message, message->nlmsg_len, 0, (struct sockaddr *) &kpeer, sizeof(kpeer)); 78 | if (!ret) { 79 | perror("sendto:"); 80 | exit(-1); 81 | } 82 | 83 | ret = recvfrom(skfd, &info, sizeof(struct u_packet_info), 0, (struct sockaddr *) &kpeer, &kpeerlen); 84 | if (!ret) { 85 | perror("recvfrom:"); 86 | exit(-1); 87 | } 88 | 89 | printf("message recvfrom kernel, content: '%s'\n", (char *) info.msg); 90 | 91 | close(skfd); 92 | return 0; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /参考资料/深入linux网络核心堆栈.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shandianchengzi/HUST_CSE_NetworkSecurity_CourseDesign/63e3b1c9d291984f9a7988e72dcdc2cd07c48c0b/参考资料/深入linux网络核心堆栈.docx -------------------------------------------------------------------------------- /参考资料/老师给的样例代码_my_firewall/Makefile: -------------------------------------------------------------------------------- 1 | #KERN_DIR = /usr/src/$(shell uname -r) 2 | KERN_DIR = /lib/modules/$(shell uname -r)/build 3 | myfw-objs := my_firewall.o #my_dev.o 4 | obj-m += myfw.o 5 | 6 | all: 7 | make -C $(KERN_DIR) M=$(shell pwd) modules 8 | clean: 9 | make -C $(KERN_DIR) M=$(shell pwd) modules clean 10 | rm -rf modules.order 11 | rm -f *.symvers 12 | -------------------------------------------------------------------------------- /参考资料/老师给的样例代码_my_firewall/my_dev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define MYMAJOR 200 6 | #define MYNAME "chardev_test_byHc" 7 | 8 | //DEVICE ID 9 | dev_t devid; 10 | struct cdev cdev; 11 | 12 | static int chardev_open(struct inode *inode, struct file *file) 13 | { 14 | printk(KERN_INFO "chardev open\n"); 15 | return 0; 16 | } 17 | 18 | static ssize_t chardev_read(struct file *file, char __user *buf, 19 | size_t size, loff_t *ppos) 20 | { 21 | return 0; 22 | } 23 | 24 | static const struct file_operations chardev_fops = { 25 | .open = chardev_open, 26 | .read = chardev_read, 27 | }; 28 | 29 | static int __init mymodule_init(void) 30 | { 31 | int ret = 0; 32 | printk(KERN_INFO "chrdev_init helloworld init\n"); 33 | cdev_init(&cdev,&chardev_fops); 34 | 35 | alloc_chrdev_region(&devid,2,255,MYNAME); 36 | printk(KERN_INFO "MAJOR Number is %d\n",MAJOR(devid)); 37 | printk(KERN_INFO "MINOR Number is %d\n",MINOR(devid)); 38 | cdev_add(&cdev,devid,255); 39 | return 0; 40 | } 41 | 42 | static void __exit mymodule_exit(void) 43 | { 44 | printk(KERN_INFO "chrdev_exit helloworld exit\n"); 45 | cdev_del(&cdev); 46 | unregister_chrdev_region(devid,255); 47 | } 48 | 49 | module_init(mymodule_init); 50 | module_exit(mymodule_exit); 51 | 52 | // MODULE_xxx这种宏作用是用来添加模块描述信息 53 | MODULE_LICENSE("GPL"); // 描述模块的许可证 54 | MODULE_AUTHOR("Hanc"); // 描述模块的作者 55 | MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息 56 | MODULE_ALIAS("alias xxx"); // 描述模块的别名信息 57 | 58 | -------------------------------------------------------------------------------- /参考资料/老师给的样例代码_my_firewall/my_firewall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | MODULE_LICENSE("GPL"); 12 | MODULE_AUTHOR("wmz"); 13 | 14 | static struct nf_hook_ops nfho; 15 | 16 | unsigned int telnet_filter(void *priv, 17 | struct sk_buff *skb, 18 | const struct nf_hook_state *state) 19 | { 20 | struct iphdr *iph; 21 | struct tcphdr *tcph; 22 | 23 | iph = ip_hdr(skb); 24 | tcph = (void *)iph + iph->ihl*4; 25 | 26 | if(iph->protocol == IPPROTO_TCP && tcph->dest == htons(23)) 27 | { 28 | printk(KERN_INFO "Dropping telnet packet to %d.%d.%d.%d\n", 29 | ((unsigned char *)&iph->daddr)[0], 30 | ((unsigned char *)&iph->daddr)[1], 31 | ((unsigned char *)&iph->daddr)[2], 32 | ((unsigned char *)&iph->daddr)[3]); 33 | return NF_DROP; 34 | } 35 | else{ 36 | return NF_ACCEPT; 37 | } 38 | } 39 | 40 | static int myfw_init(void) 41 | { 42 | printk("my firewall module loaded.\n"); 43 | 44 | nfho.hook = telnet_filter; 45 | nfho.pf = PF_INET; 46 | nfho.hooknum = NF_INET_LOCAL_OUT; 47 | nfho.priority = NF_IP_PRI_FIRST; //new version, maybe changed to NF_INET_PRI_FIRST 48 | 49 | nf_register_hook(&nfho); 50 | return 0; 51 | } 52 | 53 | static void myfw_exit(void) 54 | { 55 | printk("my firewall module exit ...\n"); 56 | nf_unregister_hook(&nfho); 57 | } 58 | 59 | module_init(myfw_init); 60 | module_exit(myfw_exit); 61 | --------------------------------------------------------------------------------