From c62da1c86dfc2735c88d3695dcffce60520da933 Mon Sep 17 00:00:00 2001 From: winPond Date: Wed, 17 Jul 2019 16:08:06 +0800 Subject: [PATCH] my solution to lab 6 --- lab/GNUmakefile | 30 + .../Backup/bc(1629).c | 151 - .../Backup/bc(3621).c | 169 - .../Backup/env(3753).c | 578 --- .../Backup/env(840).c | 577 --- .../Backup/exit(6898).c | 10 - .../Backup/faultio(2032).c | 22 - .../Backup/file(8133).c | 180 - .../Backup/fork(6291).c | 167 - .../Backup/fs(6931).c | 456 -- .../Backup/fs(7516).c | 515 -- .../Backup/init(5052).c | 180 - .../Backup/init(738).c | 172 - .../Backup/init(7465).c | 174 - .../Backup/serv(2429).c | 345 -- .../Backup/sh(7186).c | 322 -- .../Backup/spawn(5260).c | 307 -- .../Backup/trap(3657).c | 436 -- .../Backup/trap(5971).c | 430 -- .../Backup/trap(6591).c | 422 -- .../Backup/trap(6593).c | 421 -- .../Backup/trap(7420).c | 430 -- .../Untitled Project.SearchResults | 3 - .../Untitled Project.sip_sym | Bin 305432 -> 0 bytes .../Untitled Project.sip_xab | Bin 69632 -> 0 bytes .../Untitled Project.sip_xad | Bin 1008 -> 0 bytes .../Untitled Project.sip_xm | Bin 540 -> 0 bytes .../Untitled Project.sip_xsb | Bin 40960 -> 0 bytes .../Untitled Project.sip_xsd | Bin 728 -> 0 bytes .../Untitled Project.siproj | Bin 44792 -> 0 bytes .../Untitled Project.siwork | Bin 63637 -> 0 bytes .../cache/parse/boot_main.c.sisc | Bin 7018 -> 0 bytes .../cache/parse/fs_bc.c.sisc | Bin 7300 -> 0 bytes .../cache/parse/fs_fs.c.sisc | Bin 32054 -> 0 bytes .../cache/parse/fs_fs.h.sisc | Bin 20216 -> 0 bytes .../cache/parse/fs_fsformat.c.sisc | Bin 19067 -> 0 bytes .../cache/parse/fs_ide.c.sisc | Bin 7308 -> 0 bytes .../cache/parse/fs_serv.c.sisc | Bin 26776 -> 0 bytes .../cache/parse/gradelib.py.sisc | Bin 57765 -> 0 bytes .../cache/parse/inc_args.h.sisc | Bin 5387 -> 0 bytes .../cache/parse/inc_assert.h.sisc | Bin 4137 -> 0 bytes .../cache/parse/inc_env.h.sisc | Bin 5281 -> 0 bytes .../cache/parse/inc_fd.h.sisc | Bin 9620 -> 0 bytes .../cache/parse/inc_fs.h.sisc | Bin 12340 -> 0 bytes .../cache/parse/inc_lib.h.sisc | Bin 42455 -> 0 bytes .../cache/parse/inc_partition.h.sisc | Bin 3003 -> 0 bytes .../cache/parse/inc_stdarg.h.sisc | Bin 2442 -> 0 bytes .../cache/parse/inc_stdio.h.sisc | Bin 13099 -> 0 bytes .../cache/parse/inc_string.h.sisc | Bin 19591 -> 0 bytes .../cache/parse/inc_syscall.h.sisc | Bin 2828 -> 0 bytes .../cache/parse/inc_x86.h.sisc | Bin 29351 -> 0 bytes .../cache/parse/kern_console.c.sisc | Bin 23244 -> 0 bytes .../cache/parse/kern_console.h.sisc | Bin 3681 -> 0 bytes .../cache/parse/kern_cpu.h.sisc | Bin 6385 -> 0 bytes .../cache/parse/kern_env.c.sisc | Bin 21340 -> 0 bytes .../cache/parse/kern_env.h.sisc | Bin 9277 -> 0 bytes .../cache/parse/kern_init.c.sisc | Bin 8350 -> 0 bytes .../cache/parse/kern_kclock.c.sisc | Bin 2820 -> 0 bytes .../cache/parse/kern_kclock.h.sisc | Bin 4158 -> 0 bytes .../cache/parse/kern_kdebug.c.sisc | Bin 10643 -> 0 bytes .../cache/parse/kern_kdebug.h.sisc | Bin 3159 -> 0 bytes .../cache/parse/kern_monitor.c.sisc | Bin 10831 -> 0 bytes .../cache/parse/kern_monitor.h.sisc | Bin 6180 -> 0 bytes .../cache/parse/kern_mpconfig.c.sisc | Bin 15692 -> 0 bytes .../cache/parse/kern_picirq.c.sisc | Bin 2796 -> 0 bytes .../cache/parse/kern_picirq.h.sisc | Bin 3515 -> 0 bytes .../cache/parse/kern_pmap.c.sisc | Bin 51788 -> 0 bytes .../cache/parse/kern_pmap.h.sisc | Bin 21284 -> 0 bytes .../cache/parse/kern_printf.c.sisc | Bin 4214 -> 0 bytes .../cache/parse/kern_sched.c.sisc | Bin 5175 -> 0 bytes .../cache/parse/kern_sched.h.sisc | Bin 1887 -> 0 bytes .../cache/parse/kern_spinlock.c.sisc | Bin 8508 -> 0 bytes .../cache/parse/kern_spinlock.h.sisc | Bin 5818 -> 0 bytes .../cache/parse/kern_syscall.c.sisc | Bin 25792 -> 0 bytes .../cache/parse/kern_syscall.h.sisc | Bin 3963 -> 0 bytes .../cache/parse/kern_trap.c.sisc | Bin 21737 -> 0 bytes .../cache/parse/kern_trap.h.sisc | Bin 5324 -> 0 bytes .../cache/parse/lib_args.c.sisc | Bin 5940 -> 0 bytes .../cache/parse/lib_console.c.sisc | Bin 12847 -> 0 bytes .../cache/parse/lib_exit.c.sisc | Bin 1450 -> 0 bytes .../cache/parse/lib_fd.c.sisc | Bin 24174 -> 0 bytes .../cache/parse/lib_file.c.sisc | Bin 19468 -> 0 bytes .../cache/parse/lib_fprintf.c.sisc | Bin 7761 -> 0 bytes .../cache/parse/lib_ipc.c.sisc | Bin 6019 -> 0 bytes .../cache/parse/lib_libmain.c.sisc | Bin 3197 -> 0 bytes .../cache/parse/lib_pageref.c.sisc | Bin 1735 -> 0 bytes .../cache/parse/lib_panic.c.sisc | Bin 2435 -> 0 bytes .../cache/parse/lib_pgfault.c.sisc | Bin 2569 -> 0 bytes .../cache/parse/lib_pipe.c.sisc | Bin 16619 -> 0 bytes .../cache/parse/lib_printf.c.sisc | Bin 5213 -> 0 bytes .../cache/parse/lib_printfmt.c.sisc | Bin 21884 -> 0 bytes .../cache/parse/lib_readline.c.sisc | Bin 3987 -> 0 bytes .../cache/parse/lib_spawn.c.sisc | Bin 22212 -> 0 bytes .../cache/parse/lib_string.c.sisc | Bin 27516 -> 0 bytes .../cache/parse/lib_syscall.c.sisc | Bin 16728 -> 0 bytes .../cache/parse/lib_wait.c.sisc | Bin 1734 -> 0 bytes .../cache/parse/user_badsegment.c.sisc | Bin 1962 -> 0 bytes .../cache/parse/user_breakpoint.c.sisc | Bin 1958 -> 0 bytes .../cache/parse/user_buggyhello.c.sisc | Bin 1962 -> 0 bytes .../cache/parse/user_buggyhello2.c.sisc | Bin 2060 -> 0 bytes .../cache/parse/user_cat.c.sisc | Bin 4012 -> 0 bytes .../cache/parse/user_divzero.c.sisc | Bin 2059 -> 0 bytes .../cache/parse/user_dumbfork.c.sisc | Bin 5513 -> 0 bytes .../cache/parse/user_echo.c.sisc | Bin 2729 -> 0 bytes .../cache/parse/user_evilhello.c.sisc | Bin 1966 -> 0 bytes .../cache/parse/user_fairness.c.sisc | Bin 2983 -> 0 bytes .../cache/parse/user_faultalloc.c.sisc | Bin 2869 -> 0 bytes .../cache/parse/user_faultallocbad.c.sisc | Bin 2869 -> 0 bytes .../cache/parse/user_faultbadhandler.c.sisc | Bin 1974 -> 0 bytes .../cache/parse/user_faultdie.c.sisc | Bin 2851 -> 0 bytes .../cache/parse/user_faultevilhandler.c.sisc | Bin 1962 -> 0 bytes .../cache/parse/user_faultnostack.c.sisc | Bin 2303 -> 0 bytes .../cache/parse/user_faultread.c.sisc | Bin 1958 -> 0 bytes .../cache/parse/user_faultreadkernel.c.sisc | Bin 1958 -> 0 bytes .../cache/parse/user_faultregs.c.sisc | Bin 7457 -> 0 bytes .../cache/parse/user_faultwrite.c.sisc | Bin 1958 -> 0 bytes .../cache/parse/user_faultwritekernel.c.sisc | Bin 1958 -> 0 bytes .../cache/parse/user_forktree.c.sisc | Bin 4847 -> 0 bytes .../cache/parse/user_hello.c.sisc | Bin 1954 -> 0 bytes .../cache/parse/user_icode.c.sisc | Bin 2381 -> 0 bytes .../cache/parse/user_idle.c.sisc | Bin 2362 -> 0 bytes .../cache/parse/user_init.c.sisc | Bin 5444 -> 0 bytes .../cache/parse/user_initsh.c.sisc | Bin 2892 -> 0 bytes .../cache/parse/user_ls.c.sisc | Bin 10217 -> 0 bytes .../cache/parse/user_lsfd.c.sisc | Bin 3032 -> 0 bytes .../cache/parse/user_num.c.sisc | Bin 4767 -> 0 bytes .../cache/parse/user_pingpong.c.sisc | Bin 2739 -> 0 bytes .../cache/parse/user_pingpongs.c.sisc | Bin 2844 -> 0 bytes .../cache/parse/user_primes.c.sisc | Bin 3355 -> 0 bytes .../cache/parse/user_primespipe.c.sisc | Bin 4635 -> 0 bytes .../cache/parse/user_sendpage.c.sisc | Bin 2805 -> 0 bytes .../cache/parse/user_sh.c.sisc | Bin 19566 -> 0 bytes .../cache/parse/user_softint.c.sisc | Bin 1958 -> 0 bytes .../cache/parse/user_spawnfaultio.c.sisc | Bin 2044 -> 0 bytes .../cache/parse/user_spawnhello.c.sisc | Bin 2044 -> 0 bytes .../cache/parse/user_spawninit.c.sisc | Bin 2044 -> 0 bytes .../cache/parse/user_spin.c.sisc | Bin 2401 -> 0 bytes .../cache/parse/user_stresssched.c.sisc | Bin 3048 -> 0 bytes .../cache/parse/user_testbss.c.sisc | Bin 2299 -> 0 bytes .../cache/parse/user_testfdsharing.c.sisc | Bin 2957 -> 0 bytes .../cache/parse/user_testfile.c.sisc | Bin 4959 -> 0 bytes .../cache/parse/user_testkbd.c.sisc | Bin 2557 -> 0 bytes .../cache/parse/user_testmalloc.c.sisc | Bin 3094 -> 0 bytes .../cache/parse/user_testpipe.c.sisc | Bin 3651 -> 0 bytes .../cache/parse/user_testpiperace.c.sisc | Bin 3682 -> 0 bytes .../cache/parse/user_testpiperace2.c.sisc | Bin 3434 -> 0 bytes .../cache/parse/user_testptelibrary.c.sisc | Bin 3384 -> 0 bytes .../cache/parse/user_testpteshare.c.sisc | Bin 3500 -> 0 bytes .../cache/parse/user_testshell.c.sisc | Bin 5676 -> 0 bytes .../cache/parse/user_writemotd.c.sisc | Bin 2797 -> 0 bytes .../cache/parse/user_yield.c.sisc | Bin 2340 -> 0 bytes lab/conf/lab.mk | 4 +- lab/fs/Makefrag | 2 + lab/fs/bc.c | 6 + lab/fs/fs.c | 12 + lab/fs/index.html | 11 + lab/fs/serv.c | 7 +- lab/grade-lab6 | 253 + lab/inc/env.h | 1 + lab/inc/fd.h | 7 + lab/inc/lib.h | 23 + lab/inc/malloc.h | 7 + lab/inc/memlayout.h | 2 +- lab/inc/ns.h | 106 + lab/inc/syscall.h | 5 +- lab/kern/Makefrag | 15 + lab/kern/e1000.c | 150 + lab/kern/e1000.h | 135 + lab/kern/env.c | 8 +- lab/kern/init.c | 16 +- lab/kern/pci.c | 262 ++ lab/kern/pci.h | 33 + lab/kern/pcireg.h | 710 +++ lab/kern/picirq.c | 11 + lab/kern/picirq.h | 1 + lab/kern/syscall.c | 35 + lab/kern/time.c | 26 + lab/kern/time.h | 11 + lab/kern/trap.c | 5 +- lab/lab6.si4project/Backup/e1000(1028).c | 88 + lab/lab6.si4project/Backup/e1000(1108).h | 86 + lab/lab6.si4project/Backup/e1000(18).c | 88 + lab/lab6.si4project/Backup/e1000(2502).c | 59 + lab/lab6.si4project/Backup/e1000(2669).h | 86 + lab/lab6.si4project/Backup/e1000(3017).h | 131 + lab/lab6.si4project/Backup/e1000(3240).h | 94 + lab/lab6.si4project/Backup/e1000(460).c | 148 + lab/lab6.si4project/Backup/e1000(5113).h | 120 + lab/lab6.si4project/Backup/e1000(5347).h | 131 + lab/lab6.si4project/Backup/e1000(5657).c | 126 + lab/lab6.si4project/Backup/e1000(5817).c | 162 + lab/lab6.si4project/Backup/e1000(6672).c | 162 + lab/lab6.si4project/Backup/httpd(174).c | 327 ++ lab/lab6.si4project/Backup/input(2647).c | 65 + lab/lab6.si4project/Backup/input(7104).c | 16 + lab/lab6.si4project/Backup/lib(1933).h | 152 + lab/lab6.si4project/Backup/output(6468).c | 27 + lab/lab6.si4project/Backup/output(770).c | 13 + lab/lab6.si4project/Backup/pci(874).c | 262 ++ lab/lab6.si4project/Backup/syscall(2053).h | 24 + lab/lab6.si4project/Backup/syscall(3019).c | 124 + .../Backup/syscall(5120).c} | 36 +- lab/lab6.si4project/Backup/syscall(6702).c | 139 + .../Backup/syscall(6909).c} | 60 +- .../cache/parse/boot_main.c.sisc | Bin 0 -> 7018 bytes lab/lab6.si4project/cache/parse/fs_bc.c.sisc | Bin 0 -> 7180 bytes lab/lab6.si4project/cache/parse/fs_fs.c.sisc | Bin 0 -> 31526 bytes lab/lab6.si4project/cache/parse/fs_fs.h.sisc | Bin 0 -> 20216 bytes .../cache/parse/fs_fsformat.c.sisc | Bin 0 -> 19067 bytes lab/lab6.si4project/cache/parse/fs_ide.c.sisc | Bin 0 -> 7308 bytes .../cache/parse/fs_serv.c.sisc | Bin 0 -> 22652 bytes .../cache/parse/fs_test.c.sisc | Bin 2180 -> 2180 bytes .../cache/parse/gradelib.py.sisc | Bin 0 -> 57765 bytes .../cache/parse/inc_args.h.sisc | Bin 0 -> 5387 bytes .../cache/parse/inc_assert.h.sisc | Bin 0 -> 4137 bytes .../cache/parse/inc_elf.h.sisc | Bin 6217 -> 6217 bytes .../cache/parse/inc_env.h.sisc | Bin 0 -> 5477 bytes .../cache/parse/inc_error.h.sisc | Bin 2895 -> 2895 bytes lab/lab6.si4project/cache/parse/inc_fd.h.sisc | Bin 0 -> 10274 bytes lab/lab6.si4project/cache/parse/inc_fs.h.sisc | Bin 0 -> 12340 bytes .../cache/parse/inc_kbdreg.h.sisc | Bin 7606 -> 7606 bytes .../cache/parse/inc_lib.h.sisc | Bin 0 -> 64139 bytes .../cache/parse/inc_malloc.h.sisc | Bin 0 -> 2471 bytes .../cache/parse/inc_memlayout.h.sisc | Bin 5854 -> 5854 bytes .../cache/parse/inc_mmu.h.sisc | Bin 22408 -> 22408 bytes lab/lab6.si4project/cache/parse/inc_ns.h.sisc | Bin 0 -> 13101 bytes .../cache/parse/inc_partition.h.sisc | Bin 0 -> 3003 bytes .../cache/parse/inc_stab.h.sisc | Bin 4524 -> 4524 bytes .../cache/parse/inc_stdarg.h.sisc | Bin 0 -> 2442 bytes .../cache/parse/inc_stdio.h.sisc | Bin 0 -> 13099 bytes .../cache/parse/inc_string.h.sisc | Bin 0 -> 19591 bytes .../cache/parse/inc_syscall.h.sisc | Bin 0 -> 3140 bytes .../cache/parse/inc_trap.h.sisc | Bin 7702 -> 7702 bytes .../cache/parse/inc_types.h.sisc | Bin 5952 -> 5952 bytes .../cache/parse/inc_x86.h.sisc | Bin 0 -> 29351 bytes .../cache/parse/kern_console.c.sisc | Bin 0 -> 23244 bytes .../cache/parse/kern_console.h.sisc | Bin 0 -> 3681 bytes .../cache/parse/kern_cpu.h.sisc | Bin 0 -> 6385 bytes .../cache/parse/kern_e1000.c.sisc | Bin 0 -> 11273 bytes .../cache/parse/kern_e1000.h.sisc | Bin 0 -> 11018 bytes .../cache/parse/kern_entrypgdir.c.sisc | Bin 5558 -> 5558 bytes .../cache/parse/kern_env.c.sisc | Bin 0 -> 20468 bytes .../cache/parse/kern_env.h.sisc | Bin 0 -> 9277 bytes .../cache/parse/kern_init.c.sisc | Bin 0 -> 8819 bytes .../cache/parse/kern_kclock.c.sisc | Bin 0 -> 2820 bytes .../cache/parse/kern_kclock.h.sisc | Bin 0 -> 4158 bytes .../cache/parse/kern_kdebug.c.sisc | Bin 0 -> 10643 bytes .../cache/parse/kern_kdebug.h.sisc | Bin 0 -> 3159 bytes .../cache/parse/kern_lapic.c.sisc | Bin 10218 -> 10218 bytes .../cache/parse/kern_monitor.c.sisc | Bin 0 -> 10831 bytes .../cache/parse/kern_monitor.h.sisc | Bin 0 -> 6180 bytes .../cache/parse/kern_mpconfig.c.sisc | Bin 0 -> 15692 bytes .../cache/parse/kern_pci.c.sisc | Bin 0 -> 19571 bytes .../cache/parse/kern_pci.h.sisc | Bin 0 -> 5991 bytes .../cache/parse/kern_pcireg.h.sisc | Bin 0 -> 61242 bytes .../cache/parse/kern_picirq.c.sisc | Bin 0 -> 3189 bytes .../cache/parse/kern_picirq.h.sisc | Bin 0 -> 3848 bytes .../cache/parse/kern_pmap.c.sisc | Bin 0 -> 51788 bytes .../cache/parse/kern_pmap.h.sisc | Bin 0 -> 21284 bytes .../cache/parse/kern_printf.c.sisc | Bin 0 -> 4214 bytes .../cache/parse/kern_sched.c.sisc | Bin 0 -> 5175 bytes .../cache/parse/kern_sched.h.sisc | Bin 0 -> 1887 bytes .../cache/parse/kern_spinlock.c.sisc | Bin 0 -> 8508 bytes .../cache/parse/kern_spinlock.h.sisc | Bin 0 -> 5818 bytes .../cache/parse/kern_syscall.c.sisc | Bin 0 -> 27843 bytes .../cache/parse/kern_syscall.h.sisc | Bin 0 -> 3963 bytes .../cache/parse/kern_time.c.sisc | Bin 0 -> 2275 bytes .../cache/parse/kern_time.h.sisc | Bin 0 -> 2540 bytes .../cache/parse/kern_trap.c.sisc | Bin 0 -> 22583 bytes .../cache/parse/kern_trap.h.sisc | Bin 0 -> 5324 bytes .../cache/parse/lib_args.c.sisc | Bin 0 -> 5940 bytes .../cache/parse/lib_console.c.sisc | Bin 0 -> 12847 bytes .../cache/parse/lib_exit.c.sisc | Bin 0 -> 1306 bytes lab/lab6.si4project/cache/parse/lib_fd.c.sisc | Bin 0 -> 24178 bytes .../cache/parse/lib_file.c.sisc | Bin 0 -> 15900 bytes .../cache/parse/lib_fork.c.sisc | Bin 7827 -> 7467 bytes .../cache/parse/lib_fprintf.c.sisc | Bin 0 -> 7761 bytes .../cache/parse/lib_ipc.c.sisc | Bin 0 -> 6019 bytes .../cache/parse/lib_libmain.c.sisc | Bin 0 -> 3197 bytes .../cache/parse/lib_malloc.c.sisc | Bin 0 -> 7110 bytes .../cache/parse/lib_nsipc.c.sisc | Bin 0 -> 14808 bytes .../cache/parse/lib_pageref.c.sisc | Bin 0 -> 1735 bytes .../cache/parse/lib_panic.c.sisc | Bin 0 -> 2435 bytes .../cache/parse/lib_pgfault.c.sisc | Bin 0 -> 2569 bytes .../cache/parse/lib_pipe.c.sisc | Bin 0 -> 16619 bytes .../cache/parse/lib_printf.c.sisc | Bin 0 -> 5213 bytes .../cache/parse/lib_printfmt.c.sisc | Bin 0 -> 21884 bytes .../cache/parse/lib_readline.c.sisc | Bin 0 -> 3987 bytes .../cache/parse/lib_sockets.c.sisc | Bin 0 -> 20056 bytes .../cache/parse/lib_spawn.c.sisc | Bin 0 -> 19272 bytes .../cache/parse/lib_string.c.sisc | Bin 0 -> 27516 bytes .../cache/parse/lib_syscall.c.sisc | Bin 0 -> 19327 bytes .../cache/parse/lib_wait.c.sisc | Bin 0 -> 1734 bytes .../cache/parse/net_input.c.sisc | Bin 0 -> 3880 bytes lab/lab6.si4project/cache/parse/net_ns.h.sisc | Bin 0 -> 3980 bytes .../cache/parse/net_output.c.sisc | Bin 0 -> 3239 bytes .../cache/parse/net_serv.c.sisc | Bin 0 -> 25697 bytes .../cache/parse/net_testinput.c.sisc | Bin 0 -> 6922 bytes .../cache/parse/net_testoutput.c.sisc | Bin 0 -> 3402 bytes .../cache/parse/net_timer.c.sisc | Bin 0 -> 3483 bytes .../cache/parse/user_badsegment.c.sisc | Bin 0 -> 1962 bytes .../cache/parse/user_breakpoint.c.sisc | Bin 0 -> 1958 bytes .../cache/parse/user_buggyhello.c.sisc | Bin 0 -> 1962 bytes .../cache/parse/user_buggyhello2.c.sisc | Bin 0 -> 2060 bytes .../cache/parse/user_cat.c.sisc | Bin 0 -> 4012 bytes .../cache/parse/user_divzero.c.sisc | Bin 0 -> 2059 bytes .../cache/parse/user_dumbfork.c.sisc | Bin 0 -> 5513 bytes .../cache/parse/user_echo.c.sisc | Bin 0 -> 2729 bytes .../cache/parse/user_echosrv.c.sisc | Bin 0 -> 6137 bytes .../cache/parse/user_echotest.c.sisc | Bin 0 -> 4539 bytes .../cache/parse/user_evilhello.c.sisc | Bin 0 -> 1966 bytes .../cache/parse/user_fairness.c.sisc | Bin 0 -> 2983 bytes .../cache/parse/user_faultalloc.c.sisc | Bin 0 -> 2869 bytes .../cache/parse/user_faultallocbad.c.sisc | Bin 0 -> 2869 bytes .../cache/parse/user_faultbadhandler.c.sisc | Bin 0 -> 1974 bytes .../cache/parse/user_faultdie.c.sisc | Bin 0 -> 2851 bytes .../cache/parse/user_faultevilhandler.c.sisc | Bin 0 -> 1962 bytes .../cache/parse/user_faultio.c.sisc | Bin 2697 -> 2561 bytes .../cache/parse/user_faultnostack.c.sisc | Bin 0 -> 2303 bytes .../cache/parse/user_faultread.c.sisc | Bin 0 -> 1958 bytes .../cache/parse/user_faultreadkernel.c.sisc | Bin 0 -> 1958 bytes .../cache/parse/user_faultregs.c.sisc | Bin 0 -> 7457 bytes .../cache/parse/user_faultwrite.c.sisc | Bin 0 -> 1958 bytes .../cache/parse/user_faultwritekernel.c.sisc | Bin 0 -> 1958 bytes .../cache/parse/user_forktree.c.sisc | Bin 0 -> 4847 bytes .../cache/parse/user_hello.c.sisc | Bin 0 -> 1954 bytes .../cache/parse/user_httpd.c.sisc | Bin 0 -> 22553 bytes .../cache/parse/user_icode.c.sisc | Bin 0 -> 2381 bytes .../cache/parse/user_idle.c.sisc | Bin 0 -> 2362 bytes .../cache/parse/user_init.c.sisc | Bin 0 -> 5444 bytes .../cache/parse/user_initsh.c.sisc | Bin 0 -> 2892 bytes .../cache/parse/user_ls.c.sisc | Bin 0 -> 10217 bytes .../cache/parse/user_lsfd.c.sisc | Bin 0 -> 3032 bytes .../cache/parse/user_num.c.sisc | Bin 0 -> 4767 bytes .../cache/parse/user_pingpong.c.sisc | Bin 0 -> 2739 bytes .../cache/parse/user_pingpongs.c.sisc | Bin 0 -> 2844 bytes .../cache/parse/user_primes.c.sisc | Bin 0 -> 3355 bytes .../cache/parse/user_primespipe.c.sisc | Bin 0 -> 4635 bytes .../cache/parse/user_sendpage.c.sisc | Bin 0 -> 2805 bytes .../cache/parse/user_sh.c.sisc | Bin 0 -> 19070 bytes .../cache/parse/user_softint.c.sisc | Bin 0 -> 1958 bytes .../cache/parse/user_spawnfaultio.c.sisc | Bin 0 -> 2044 bytes .../cache/parse/user_spawnhello.c.sisc | Bin 0 -> 2044 bytes .../cache/parse/user_spawninit.c.sisc | Bin 0 -> 2044 bytes .../cache/parse/user_spin.c.sisc | Bin 0 -> 2401 bytes .../cache/parse/user_stresssched.c.sisc | Bin 0 -> 3048 bytes .../cache/parse/user_testbss.c.sisc | Bin 0 -> 2299 bytes .../cache/parse/user_testfdsharing.c.sisc | Bin 0 -> 2957 bytes .../cache/parse/user_testfile.c.sisc | Bin 0 -> 4959 bytes .../cache/parse/user_testkbd.c.sisc | Bin 0 -> 2557 bytes .../cache/parse/user_testmalloc.c.sisc | Bin 0 -> 3094 bytes .../cache/parse/user_testpipe.c.sisc | Bin 0 -> 3651 bytes .../cache/parse/user_testpiperace.c.sisc | Bin 0 -> 3682 bytes .../cache/parse/user_testpiperace2.c.sisc | Bin 0 -> 3434 bytes .../cache/parse/user_testptelibrary.c.sisc | Bin 0 -> 3384 bytes .../cache/parse/user_testpteshare.c.sisc | Bin 0 -> 3500 bytes .../cache/parse/user_testshell.c.sisc | Bin 0 -> 5676 bytes .../cache/parse/user_testtime.c.sisc | Bin 0 -> 3364 bytes .../cache/parse/user_writemotd.c.sisc | Bin 0 -> 2797 bytes .../cache/parse/user_yield.c.sisc | Bin 0 -> 2340 bytes .../lab6.bookmarks.xml} | 14 +- lab/lab6.si4project/lab6.sip_sym | Bin 0 -> 1467512 bytes lab/lab6.si4project/lab6.sip_xab | Bin 0 -> 131072 bytes lab/lab6.si4project/lab6.sip_xad | Bin 0 -> 1608 bytes .../lab6.sip_xc} | Bin 273752 -> 324352 bytes .../lab6.sip_xf} | Bin 273752 -> 324352 bytes lab/lab6.si4project/lab6.sip_xm | Bin 0 -> 652 bytes .../lab6.sip_xr} | Bin 273752 -> 324352 bytes lab/lab6.si4project/lab6.sip_xsb | Bin 0 -> 61440 bytes lab/lab6.si4project/lab6.sip_xsd | Bin 0 -> 928 bytes lab/lab6.si4project/lab6.siproj | Bin 0 -> 53976 bytes .../lab6.siproj_settings.xml} | 44 +- lab/lab6.si4project/lab6.siwork | Bin 0 -> 90385 bytes .../lab6.snippets.xml} | 0 lab/lib/Makefrag | 4 + lab/lib/fd.c | 1 + lab/lib/file.c | 2 + lab/lib/malloc.c | 140 + lab/lib/nsipc.c | 123 + lab/lib/sockets.c | 132 + lab/lib/spawn.c | 3 + lab/lib/syscall.c | 19 + lab/net/Makefrag | 27 + lab/net/input.c | 53 + lab/net/lwip/FILES | 13 + lab/net/lwip/Makefrag | 73 + lab/net/lwip/api/api_lib.c | 571 +++ lab/net/lwip/api/api_msg.c | 1210 +++++ lab/net/lwip/api/err.c | 74 + lab/net/lwip/api/netbuf.c | 235 + lab/net/lwip/api/netdb.c | 353 ++ lab/net/lwip/api/netifapi.c | 126 + lab/net/lwip/api/sockets.c | 1924 ++++++++ lab/net/lwip/api/tcpip.c | 559 +++ lab/net/lwip/core/dhcp.c | 1553 +++++++ lab/net/lwip/core/dns.c | 814 ++++ lab/net/lwip/core/init.c | 253 + lab/net/lwip/core/ipv4/autoip.c | 432 ++ lab/net/lwip/core/ipv4/icmp.c | 317 ++ lab/net/lwip/core/ipv4/igmp.c | 808 ++++ lab/net/lwip/core/ipv4/inet.c | 278 ++ lab/net/lwip/core/ipv4/inet_chksum.c | 426 ++ lab/net/lwip/core/ipv4/ip.c | 624 +++ lab/net/lwip/core/ipv4/ip_addr.c | 84 + lab/net/lwip/core/ipv4/ip_frag.c | 783 ++++ lab/net/lwip/core/ipv6/README | 1 + lab/net/lwip/core/ipv6/icmp6.c | 179 + lab/net/lwip/core/ipv6/inet6.c | 163 + lab/net/lwip/core/ipv6/ip6.c | 375 ++ lab/net/lwip/core/ipv6/ip6_addr.c | 72 + lab/net/lwip/core/mem.c | 631 +++ lab/net/lwip/core/memp.c | 370 ++ lab/net/lwip/core/netif.c | 663 +++ lab/net/lwip/core/pbuf.c | 779 ++++ lab/net/lwip/core/raw.c | 336 ++ lab/net/lwip/core/snmp/asn1_dec.c | 657 +++ lab/net/lwip/core/snmp/asn1_enc.c | 611 +++ lab/net/lwip/core/snmp/mib2.c | 4126 +++++++++++++++++ lab/net/lwip/core/snmp/mib_structs.c | 1183 +++++ lab/net/lwip/core/snmp/msg_in.c | 1453 ++++++ lab/net/lwip/core/snmp/msg_out.c | 683 +++ lab/net/lwip/core/stats.c | 149 + lab/net/lwip/core/sys.c | 344 ++ lab/net/lwip/core/tcp.c | 1420 ++++++ lab/net/lwip/core/tcp_in.c | 1350 ++++++ lab/net/lwip/core/tcp_out.c | 953 ++++ lab/net/lwip/core/udp.c | 824 ++++ lab/net/lwip/include/ipv4/lwip/autoip.h | 105 + lab/net/lwip/include/ipv4/lwip/icmp.h | 121 + lab/net/lwip/include/ipv4/lwip/igmp.h | 162 + lab/net/lwip/include/ipv4/lwip/inet.h | 100 + lab/net/lwip/include/ipv4/lwip/inet_chksum.h | 58 + lab/net/lwip/include/ipv4/lwip/ip.h | 173 + lab/net/lwip/include/ipv4/lwip/ip_addr.h | 165 + lab/net/lwip/include/ipv4/lwip/ip_frag.h | 76 + lab/net/lwip/include/ipv6/lwip/icmp.h | 100 + lab/net/lwip/include/ipv6/lwip/inet.h | 68 + lab/net/lwip/include/ipv6/lwip/ip.h | 127 + lab/net/lwip/include/ipv6/lwip/ip_addr.h | 97 + lab/net/lwip/include/lwip/api.h | 217 + lab/net/lwip/include/lwip/api_msg.h | 160 + lab/net/lwip/include/lwip/arch.h | 228 + lab/net/lwip/include/lwip/debug.h | 97 + lab/net/lwip/include/lwip/def.h | 47 + lab/net/lwip/include/lwip/dhcp.h | 246 + lab/net/lwip/include/lwip/dns.h | 92 + lab/net/lwip/include/lwip/err.h | 87 + lab/net/lwip/include/lwip/init.h | 48 + lab/net/lwip/include/lwip/mem.h | 103 + lab/net/lwip/include/lwip/memp.h | 94 + lab/net/lwip/include/lwip/memp_std.h | 101 + lab/net/lwip/include/lwip/netbuf.h | 76 + lab/net/lwip/include/lwip/netdb.h | 109 + lab/net/lwip/include/lwip/netif.h | 263 ++ lab/net/lwip/include/lwip/netifapi.h | 100 + lab/net/lwip/include/lwip/opt.h | 1704 +++++++ lab/net/lwip/include/lwip/pbuf.h | 118 + lab/net/lwip/include/lwip/raw.h | 97 + lab/net/lwip/include/lwip/sio.h | 81 + lab/net/lwip/include/lwip/snmp.h | 364 ++ lab/net/lwip/include/lwip/snmp_asn1.h | 97 + lab/net/lwip/include/lwip/snmp_msg.h | 307 ++ lab/net/lwip/include/lwip/snmp_structs.h | 262 ++ lab/net/lwip/include/lwip/sockets.h | 358 ++ lab/net/lwip/include/lwip/stats.h | 283 ++ lab/net/lwip/include/lwip/sys.h | 246 + lab/net/lwip/include/lwip/tcp.h | 641 +++ lab/net/lwip/include/lwip/tcpip.h | 139 + lab/net/lwip/include/lwip/udp.h | 150 + lab/net/lwip/include/netif/etharp.h | 175 + lab/net/lwip/include/netif/loopif.h | 53 + lab/net/lwip/include/netif/ppp_oe.h | 161 + lab/net/lwip/include/netif/slipif.h | 50 + lab/net/lwip/jos/arch/cc.h | 41 + lab/net/lwip/jos/arch/i386/setjmp.h | 17 + lab/net/lwip/jos/arch/longjmp.S | 39 + lab/net/lwip/jos/arch/perf.h | 7 + lab/net/lwip/jos/arch/perror.c | 166 + lab/net/lwip/jos/arch/perror.h | 7 + lab/net/lwip/jos/arch/queue.h | 219 + lab/net/lwip/jos/arch/setjmp.h | 10 + lab/net/lwip/jos/arch/sys_arch.c | 322 ++ lab/net/lwip/jos/arch/sys_arch.h | 23 + lab/net/lwip/jos/arch/thread.c | 188 + lab/net/lwip/jos/arch/thread.h | 19 + lab/net/lwip/jos/arch/threadq.h | 65 + lab/net/lwip/jos/jif/jif.c | 255 + lab/net/lwip/jos/jif/jif.h | 4 + lab/net/lwip/jos/lwipopts.h | 63 + lab/net/lwip/netif/FILES | 25 + lab/net/lwip/netif/etharp.c | 1186 +++++ lab/net/lwip/netif/ethernetif.c | 313 ++ lab/net/lwip/netif/loopif.c | 66 + lab/net/lwip/netif/ppp/auth.c | 988 ++++ lab/net/lwip/netif/ppp/auth.h | 111 + lab/net/lwip/netif/ppp/chap.c | 902 ++++ lab/net/lwip/netif/ppp/chap.h | 166 + lab/net/lwip/netif/ppp/chpms.c | 396 ++ lab/net/lwip/netif/ppp/chpms.h | 64 + lab/net/lwip/netif/ppp/fsm.c | 906 ++++ lab/net/lwip/netif/ppp/fsm.h | 169 + lab/net/lwip/netif/ppp/ipcp.c | 1440 ++++++ lab/net/lwip/netif/ppp/ipcp.h | 124 + lab/net/lwip/netif/ppp/lcp.c | 2035 ++++++++ lab/net/lwip/netif/ppp/lcp.h | 167 + lab/net/lwip/netif/ppp/magic.c | 82 + lab/net/lwip/netif/ppp/magic.h | 67 + lab/net/lwip/netif/ppp/md5.c | 318 ++ lab/net/lwip/netif/ppp/md5.h | 55 + lab/net/lwip/netif/ppp/pap.c | 617 +++ lab/net/lwip/netif/ppp/pap.h | 131 + lab/net/lwip/netif/ppp/ppp.c | 2003 ++++++++ lab/net/lwip/netif/ppp/ppp.h | 465 ++ lab/net/lwip/netif/ppp/ppp_oe.c | 1227 +++++ lab/net/lwip/netif/ppp/pppdebug.h | 86 + lab/net/lwip/netif/ppp/randm.c | 248 + lab/net/lwip/netif/ppp/randm.h | 81 + lab/net/lwip/netif/ppp/vj.c | 660 +++ lab/net/lwip/netif/ppp/vj.h | 155 + lab/net/lwip/netif/ppp/vjbsdhdr.h | 75 + lab/net/lwip/netif/slipif.c | 279 ++ lab/net/ns.h | 22 + lab/net/output.c | 27 + lab/net/serv.c | 356 ++ lab/net/testinput.c | 115 + lab/net/testoutput.c | 42 + lab/net/timer.c | 32 + lab/user/Makefrag | 1 + lab/user/echosrv.c | 89 + lab/user/echotest.c | 68 + lab/user/faultio.c | 2 + lab/user/httpd.c | 354 ++ lab/user/sh.c | 6 +- lab/user/testtime.c | 35 + 534 files changed, 60875 insertions(+), 6513 deletions(-) delete mode 100644 lab/Untitled Project.si4project/Backup/bc(1629).c delete mode 100644 lab/Untitled Project.si4project/Backup/bc(3621).c delete mode 100644 lab/Untitled Project.si4project/Backup/env(3753).c delete mode 100644 lab/Untitled Project.si4project/Backup/env(840).c delete mode 100644 lab/Untitled Project.si4project/Backup/exit(6898).c delete mode 100644 lab/Untitled Project.si4project/Backup/faultio(2032).c delete mode 100644 lab/Untitled Project.si4project/Backup/file(8133).c delete mode 100644 lab/Untitled Project.si4project/Backup/fork(6291).c delete mode 100644 lab/Untitled Project.si4project/Backup/fs(6931).c delete mode 100644 lab/Untitled Project.si4project/Backup/fs(7516).c delete mode 100644 lab/Untitled Project.si4project/Backup/init(5052).c delete mode 100644 lab/Untitled Project.si4project/Backup/init(738).c delete mode 100644 lab/Untitled Project.si4project/Backup/init(7465).c delete mode 100644 lab/Untitled Project.si4project/Backup/serv(2429).c delete mode 100644 lab/Untitled Project.si4project/Backup/sh(7186).c delete mode 100644 lab/Untitled Project.si4project/Backup/spawn(5260).c delete mode 100644 lab/Untitled Project.si4project/Backup/trap(3657).c delete mode 100644 lab/Untitled Project.si4project/Backup/trap(5971).c delete mode 100644 lab/Untitled Project.si4project/Backup/trap(6591).c delete mode 100644 lab/Untitled Project.si4project/Backup/trap(6593).c delete mode 100644 lab/Untitled Project.si4project/Backup/trap(7420).c delete mode 100644 lab/Untitled Project.si4project/Untitled Project.SearchResults delete mode 100644 lab/Untitled Project.si4project/Untitled Project.sip_sym delete mode 100644 lab/Untitled Project.si4project/Untitled Project.sip_xab delete mode 100644 lab/Untitled Project.si4project/Untitled Project.sip_xad delete mode 100644 lab/Untitled Project.si4project/Untitled Project.sip_xm delete mode 100644 lab/Untitled Project.si4project/Untitled Project.sip_xsb delete mode 100644 lab/Untitled Project.si4project/Untitled Project.sip_xsd delete mode 100644 lab/Untitled Project.si4project/Untitled Project.siproj delete mode 100644 lab/Untitled Project.si4project/Untitled Project.siwork delete mode 100644 lab/Untitled Project.si4project/cache/parse/boot_main.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/fs_bc.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/fs_fs.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/fs_fs.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/fs_fsformat.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/fs_ide.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/fs_serv.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/gradelib.py.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_args.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_assert.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_env.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_fd.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_fs.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_lib.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_partition.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_stdarg.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_stdio.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_string.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_syscall.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/inc_x86.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_console.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_console.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_cpu.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_env.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_env.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_init.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_kclock.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_kclock.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_kdebug.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_kdebug.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_monitor.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_monitor.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_mpconfig.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_picirq.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_picirq.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_pmap.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_pmap.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_printf.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_sched.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_sched.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_spinlock.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_spinlock.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_syscall.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_syscall.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_trap.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/kern_trap.h.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_args.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_console.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_exit.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_fd.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_file.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_fprintf.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_ipc.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_libmain.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_pageref.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_panic.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_pgfault.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_pipe.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_printf.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_printfmt.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_readline.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_spawn.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_string.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_syscall.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/lib_wait.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_badsegment.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_breakpoint.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_buggyhello.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_buggyhello2.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_cat.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_divzero.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_dumbfork.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_echo.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_evilhello.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_fairness.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultalloc.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultallocbad.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultbadhandler.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultdie.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultevilhandler.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultnostack.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultread.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultreadkernel.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultregs.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultwrite.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_faultwritekernel.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_forktree.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_hello.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_icode.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_idle.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_init.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_initsh.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_ls.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_lsfd.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_num.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_pingpong.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_pingpongs.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_primes.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_primespipe.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_sendpage.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_sh.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_softint.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_spawnfaultio.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_spawnhello.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_spawninit.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_spin.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_stresssched.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testbss.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testfdsharing.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testfile.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testkbd.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testmalloc.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testpipe.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testpiperace.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testpiperace2.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testptelibrary.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testpteshare.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_testshell.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_writemotd.c.sisc delete mode 100644 lab/Untitled Project.si4project/cache/parse/user_yield.c.sisc create mode 100644 lab/fs/index.html create mode 100644 lab/grade-lab6 create mode 100644 lab/inc/malloc.h create mode 100644 lab/inc/ns.h create mode 100644 lab/kern/e1000.c create mode 100644 lab/kern/e1000.h create mode 100644 lab/kern/pci.c create mode 100644 lab/kern/pci.h create mode 100644 lab/kern/pcireg.h create mode 100644 lab/kern/time.c create mode 100644 lab/kern/time.h create mode 100644 lab/lab6.si4project/Backup/e1000(1028).c create mode 100644 lab/lab6.si4project/Backup/e1000(1108).h create mode 100644 lab/lab6.si4project/Backup/e1000(18).c create mode 100644 lab/lab6.si4project/Backup/e1000(2502).c create mode 100644 lab/lab6.si4project/Backup/e1000(2669).h create mode 100644 lab/lab6.si4project/Backup/e1000(3017).h create mode 100644 lab/lab6.si4project/Backup/e1000(3240).h create mode 100644 lab/lab6.si4project/Backup/e1000(460).c create mode 100644 lab/lab6.si4project/Backup/e1000(5113).h create mode 100644 lab/lab6.si4project/Backup/e1000(5347).h create mode 100644 lab/lab6.si4project/Backup/e1000(5657).c create mode 100644 lab/lab6.si4project/Backup/e1000(5817).c create mode 100644 lab/lab6.si4project/Backup/e1000(6672).c create mode 100644 lab/lab6.si4project/Backup/httpd(174).c create mode 100644 lab/lab6.si4project/Backup/input(2647).c create mode 100644 lab/lab6.si4project/Backup/input(7104).c create mode 100644 lab/lab6.si4project/Backup/lib(1933).h create mode 100644 lab/lab6.si4project/Backup/output(6468).c create mode 100644 lab/lab6.si4project/Backup/output(770).c create mode 100644 lab/lab6.si4project/Backup/pci(874).c create mode 100644 lab/lab6.si4project/Backup/syscall(2053).h create mode 100644 lab/lab6.si4project/Backup/syscall(3019).c rename lab/{Untitled Project.si4project/Backup/syscall(8111).c => lab6.si4project/Backup/syscall(5120).c} (95%) create mode 100644 lab/lab6.si4project/Backup/syscall(6702).c rename lab/{Untitled Project.si4project/Backup/syscall(7911).c => lab6.si4project/Backup/syscall(6909).c} (91%) create mode 100644 lab/lab6.si4project/cache/parse/boot_main.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/fs_bc.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/fs_fs.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/fs_fs.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/fs_fsformat.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/fs_ide.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/fs_serv.c.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/fs_test.c.sisc (51%) create mode 100644 lab/lab6.si4project/cache/parse/gradelib.py.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_args.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_assert.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_elf.h.sisc (75%) create mode 100644 lab/lab6.si4project/cache/parse/inc_env.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_error.h.sisc (71%) create mode 100644 lab/lab6.si4project/cache/parse/inc_fd.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_fs.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_kbdreg.h.sisc (94%) create mode 100644 lab/lab6.si4project/cache/parse/inc_lib.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_malloc.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_memlayout.h.sisc (74%) rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_mmu.h.sisc (71%) create mode 100644 lab/lab6.si4project/cache/parse/inc_ns.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_partition.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_stab.h.sisc (86%) create mode 100644 lab/lab6.si4project/cache/parse/inc_stdarg.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_stdio.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_string.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/inc_syscall.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_trap.h.sisc (84%) rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/inc_types.h.sisc (52%) create mode 100644 lab/lab6.si4project/cache/parse/inc_x86.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_console.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_console.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_cpu.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_e1000.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_e1000.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/kern_entrypgdir.c.sisc (84%) create mode 100644 lab/lab6.si4project/cache/parse/kern_env.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_env.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_init.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_kclock.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_kclock.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_kdebug.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_kdebug.h.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/kern_lapic.c.sisc (50%) create mode 100644 lab/lab6.si4project/cache/parse/kern_monitor.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_monitor.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_mpconfig.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_pci.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_pci.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_pcireg.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_picirq.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_picirq.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_pmap.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_pmap.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_printf.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_sched.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_sched.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_spinlock.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_spinlock.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_syscall.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_syscall.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_time.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_time.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_trap.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/kern_trap.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_args.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_console.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_exit.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_fd.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_file.c.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/lib_fork.c.sisc (51%) create mode 100644 lab/lab6.si4project/cache/parse/lib_fprintf.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_ipc.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_libmain.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_malloc.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_nsipc.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_pageref.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_panic.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_pgfault.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_pipe.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_printf.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_printfmt.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_readline.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_sockets.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_spawn.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_string.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_syscall.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/lib_wait.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_input.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_ns.h.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_output.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_serv.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_testinput.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_testoutput.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/net_timer.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_badsegment.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_breakpoint.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_buggyhello.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_buggyhello2.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_cat.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_divzero.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_dumbfork.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_echo.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_echosrv.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_echotest.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_evilhello.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_fairness.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultalloc.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultallocbad.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultbadhandler.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultdie.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultevilhandler.c.sisc rename lab/{Untitled Project.si4project => lab6.si4project}/cache/parse/user_faultio.c.sisc (59%) create mode 100644 lab/lab6.si4project/cache/parse/user_faultnostack.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultread.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultreadkernel.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultregs.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultwrite.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_faultwritekernel.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_forktree.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_hello.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_httpd.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_icode.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_idle.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_init.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_initsh.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_ls.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_lsfd.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_num.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_pingpong.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_pingpongs.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_primes.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_primespipe.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_sendpage.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_sh.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_softint.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_spawnfaultio.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_spawnhello.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_spawninit.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_spin.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_stresssched.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testbss.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testfdsharing.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testfile.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testkbd.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testmalloc.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testpipe.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testpiperace.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testpiperace2.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testptelibrary.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testpteshare.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testshell.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_testtime.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_writemotd.c.sisc create mode 100644 lab/lab6.si4project/cache/parse/user_yield.c.sisc rename lab/{Untitled Project.si4project/Untitled Project.bookmarks.xml => lab6.si4project/lab6.bookmarks.xml} (95%) create mode 100644 lab/lab6.si4project/lab6.sip_sym create mode 100644 lab/lab6.si4project/lab6.sip_xab create mode 100644 lab/lab6.si4project/lab6.sip_xad rename lab/{Untitled Project.si4project/Untitled Project.sip_xc => lab6.si4project/lab6.sip_xc} (54%) rename lab/{Untitled Project.si4project/Untitled Project.sip_xf => lab6.si4project/lab6.sip_xf} (51%) create mode 100644 lab/lab6.si4project/lab6.sip_xm rename lab/{Untitled Project.si4project/Untitled Project.sip_xr => lab6.si4project/lab6.sip_xr} (52%) create mode 100644 lab/lab6.si4project/lab6.sip_xsb create mode 100644 lab/lab6.si4project/lab6.sip_xsd create mode 100644 lab/lab6.si4project/lab6.siproj rename lab/{Untitled Project.si4project/Untitled Project.siproj_settings.xml => lab6.si4project/lab6.siproj_settings.xml} (95%) create mode 100644 lab/lab6.si4project/lab6.siwork rename lab/{Untitled Project.si4project/Untitled Project.snippets.xml => lab6.si4project/lab6.snippets.xml} (100%) create mode 100644 lab/lib/malloc.c create mode 100644 lab/lib/nsipc.c create mode 100644 lab/lib/sockets.c create mode 100644 lab/net/Makefrag create mode 100644 lab/net/input.c create mode 100644 lab/net/lwip/FILES create mode 100644 lab/net/lwip/Makefrag create mode 100644 lab/net/lwip/api/api_lib.c create mode 100644 lab/net/lwip/api/api_msg.c create mode 100644 lab/net/lwip/api/err.c create mode 100644 lab/net/lwip/api/netbuf.c create mode 100644 lab/net/lwip/api/netdb.c create mode 100644 lab/net/lwip/api/netifapi.c create mode 100644 lab/net/lwip/api/sockets.c create mode 100644 lab/net/lwip/api/tcpip.c create mode 100644 lab/net/lwip/core/dhcp.c create mode 100644 lab/net/lwip/core/dns.c create mode 100644 lab/net/lwip/core/init.c create mode 100644 lab/net/lwip/core/ipv4/autoip.c create mode 100644 lab/net/lwip/core/ipv4/icmp.c create mode 100644 lab/net/lwip/core/ipv4/igmp.c create mode 100644 lab/net/lwip/core/ipv4/inet.c create mode 100644 lab/net/lwip/core/ipv4/inet_chksum.c create mode 100644 lab/net/lwip/core/ipv4/ip.c create mode 100644 lab/net/lwip/core/ipv4/ip_addr.c create mode 100644 lab/net/lwip/core/ipv4/ip_frag.c create mode 100644 lab/net/lwip/core/ipv6/README create mode 100644 lab/net/lwip/core/ipv6/icmp6.c create mode 100644 lab/net/lwip/core/ipv6/inet6.c create mode 100644 lab/net/lwip/core/ipv6/ip6.c create mode 100644 lab/net/lwip/core/ipv6/ip6_addr.c create mode 100644 lab/net/lwip/core/mem.c create mode 100644 lab/net/lwip/core/memp.c create mode 100644 lab/net/lwip/core/netif.c create mode 100644 lab/net/lwip/core/pbuf.c create mode 100644 lab/net/lwip/core/raw.c create mode 100644 lab/net/lwip/core/snmp/asn1_dec.c create mode 100644 lab/net/lwip/core/snmp/asn1_enc.c create mode 100644 lab/net/lwip/core/snmp/mib2.c create mode 100644 lab/net/lwip/core/snmp/mib_structs.c create mode 100644 lab/net/lwip/core/snmp/msg_in.c create mode 100644 lab/net/lwip/core/snmp/msg_out.c create mode 100644 lab/net/lwip/core/stats.c create mode 100644 lab/net/lwip/core/sys.c create mode 100644 lab/net/lwip/core/tcp.c create mode 100644 lab/net/lwip/core/tcp_in.c create mode 100644 lab/net/lwip/core/tcp_out.c create mode 100644 lab/net/lwip/core/udp.c create mode 100644 lab/net/lwip/include/ipv4/lwip/autoip.h create mode 100644 lab/net/lwip/include/ipv4/lwip/icmp.h create mode 100644 lab/net/lwip/include/ipv4/lwip/igmp.h create mode 100644 lab/net/lwip/include/ipv4/lwip/inet.h create mode 100644 lab/net/lwip/include/ipv4/lwip/inet_chksum.h create mode 100644 lab/net/lwip/include/ipv4/lwip/ip.h create mode 100644 lab/net/lwip/include/ipv4/lwip/ip_addr.h create mode 100644 lab/net/lwip/include/ipv4/lwip/ip_frag.h create mode 100644 lab/net/lwip/include/ipv6/lwip/icmp.h create mode 100644 lab/net/lwip/include/ipv6/lwip/inet.h create mode 100644 lab/net/lwip/include/ipv6/lwip/ip.h create mode 100644 lab/net/lwip/include/ipv6/lwip/ip_addr.h create mode 100644 lab/net/lwip/include/lwip/api.h create mode 100644 lab/net/lwip/include/lwip/api_msg.h create mode 100644 lab/net/lwip/include/lwip/arch.h create mode 100644 lab/net/lwip/include/lwip/debug.h create mode 100644 lab/net/lwip/include/lwip/def.h create mode 100644 lab/net/lwip/include/lwip/dhcp.h create mode 100644 lab/net/lwip/include/lwip/dns.h create mode 100644 lab/net/lwip/include/lwip/err.h create mode 100644 lab/net/lwip/include/lwip/init.h create mode 100644 lab/net/lwip/include/lwip/mem.h create mode 100644 lab/net/lwip/include/lwip/memp.h create mode 100644 lab/net/lwip/include/lwip/memp_std.h create mode 100644 lab/net/lwip/include/lwip/netbuf.h create mode 100644 lab/net/lwip/include/lwip/netdb.h create mode 100644 lab/net/lwip/include/lwip/netif.h create mode 100644 lab/net/lwip/include/lwip/netifapi.h create mode 100644 lab/net/lwip/include/lwip/opt.h create mode 100644 lab/net/lwip/include/lwip/pbuf.h create mode 100644 lab/net/lwip/include/lwip/raw.h create mode 100644 lab/net/lwip/include/lwip/sio.h create mode 100644 lab/net/lwip/include/lwip/snmp.h create mode 100644 lab/net/lwip/include/lwip/snmp_asn1.h create mode 100644 lab/net/lwip/include/lwip/snmp_msg.h create mode 100644 lab/net/lwip/include/lwip/snmp_structs.h create mode 100644 lab/net/lwip/include/lwip/sockets.h create mode 100644 lab/net/lwip/include/lwip/stats.h create mode 100644 lab/net/lwip/include/lwip/sys.h create mode 100644 lab/net/lwip/include/lwip/tcp.h create mode 100644 lab/net/lwip/include/lwip/tcpip.h create mode 100644 lab/net/lwip/include/lwip/udp.h create mode 100644 lab/net/lwip/include/netif/etharp.h create mode 100644 lab/net/lwip/include/netif/loopif.h create mode 100644 lab/net/lwip/include/netif/ppp_oe.h create mode 100644 lab/net/lwip/include/netif/slipif.h create mode 100644 lab/net/lwip/jos/arch/cc.h create mode 100644 lab/net/lwip/jos/arch/i386/setjmp.h create mode 100644 lab/net/lwip/jos/arch/longjmp.S create mode 100644 lab/net/lwip/jos/arch/perf.h create mode 100644 lab/net/lwip/jos/arch/perror.c create mode 100644 lab/net/lwip/jos/arch/perror.h create mode 100644 lab/net/lwip/jos/arch/queue.h create mode 100644 lab/net/lwip/jos/arch/setjmp.h create mode 100644 lab/net/lwip/jos/arch/sys_arch.c create mode 100644 lab/net/lwip/jos/arch/sys_arch.h create mode 100644 lab/net/lwip/jos/arch/thread.c create mode 100644 lab/net/lwip/jos/arch/thread.h create mode 100644 lab/net/lwip/jos/arch/threadq.h create mode 100644 lab/net/lwip/jos/jif/jif.c create mode 100644 lab/net/lwip/jos/jif/jif.h create mode 100644 lab/net/lwip/jos/lwipopts.h create mode 100644 lab/net/lwip/netif/FILES create mode 100644 lab/net/lwip/netif/etharp.c create mode 100644 lab/net/lwip/netif/ethernetif.c create mode 100644 lab/net/lwip/netif/loopif.c create mode 100644 lab/net/lwip/netif/ppp/auth.c create mode 100644 lab/net/lwip/netif/ppp/auth.h create mode 100644 lab/net/lwip/netif/ppp/chap.c create mode 100644 lab/net/lwip/netif/ppp/chap.h create mode 100644 lab/net/lwip/netif/ppp/chpms.c create mode 100644 lab/net/lwip/netif/ppp/chpms.h create mode 100644 lab/net/lwip/netif/ppp/fsm.c create mode 100644 lab/net/lwip/netif/ppp/fsm.h create mode 100644 lab/net/lwip/netif/ppp/ipcp.c create mode 100644 lab/net/lwip/netif/ppp/ipcp.h create mode 100644 lab/net/lwip/netif/ppp/lcp.c create mode 100644 lab/net/lwip/netif/ppp/lcp.h create mode 100644 lab/net/lwip/netif/ppp/magic.c create mode 100644 lab/net/lwip/netif/ppp/magic.h create mode 100644 lab/net/lwip/netif/ppp/md5.c create mode 100644 lab/net/lwip/netif/ppp/md5.h create mode 100644 lab/net/lwip/netif/ppp/pap.c create mode 100644 lab/net/lwip/netif/ppp/pap.h create mode 100644 lab/net/lwip/netif/ppp/ppp.c create mode 100644 lab/net/lwip/netif/ppp/ppp.h create mode 100644 lab/net/lwip/netif/ppp/ppp_oe.c create mode 100644 lab/net/lwip/netif/ppp/pppdebug.h create mode 100644 lab/net/lwip/netif/ppp/randm.c create mode 100644 lab/net/lwip/netif/ppp/randm.h create mode 100644 lab/net/lwip/netif/ppp/vj.c create mode 100644 lab/net/lwip/netif/ppp/vj.h create mode 100644 lab/net/lwip/netif/ppp/vjbsdhdr.h create mode 100644 lab/net/lwip/netif/slipif.c create mode 100644 lab/net/ns.h create mode 100644 lab/net/output.c create mode 100644 lab/net/serv.c create mode 100644 lab/net/testinput.c create mode 100644 lab/net/testoutput.c create mode 100644 lab/net/timer.c create mode 100644 lab/user/echosrv.c create mode 100644 lab/user/echotest.c create mode 100644 lab/user/httpd.c create mode 100644 lab/user/testtime.c diff --git a/lab/GNUmakefile b/lab/GNUmakefile index 2b85d1d..aebcc32 100644 --- a/lab/GNUmakefile +++ b/lab/GNUmakefile @@ -94,6 +94,10 @@ CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32 # mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2 CFLAGS += -fno-tree-ch +CFLAGS += -I$(TOP)/net/lwip/include \ + -I$(TOP)/net/lwip/include/ipv4 \ + -I$(TOP)/net/lwip/jos + # Add -fno-stack-protector if the option exists. CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) @@ -142,16 +146,22 @@ include kern/Makefrag include lib/Makefrag include user/Makefrag include fs/Makefrag +include net/Makefrag CPUS ?= 1 +PORT7 := $(shell expr $(GDBPORT) + 1) +PORT80 := $(shell expr $(GDBPORT) + 2) + QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::$(GDBPORT) QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi) IMAGES = $(OBJDIR)/kern/kernel.img QEMUOPTS += -smp $(CPUS) QEMUOPTS += -drive file=$(OBJDIR)/fs/fs.img,index=1,media=disk,format=raw IMAGES += $(OBJDIR)/fs/fs.img +QEMUOPTS += -net user -net nic,model=e1000 -redir tcp:$(PORT7)::7 \ + -redir tcp:$(PORT80)::80 -redir udp:$(PORT7)::7 -net dump,file=qemu.pcap QEMUOPTS += $(QEMUEXTRA) .gdbinit: .gdbinit.tmpl @@ -161,6 +171,8 @@ gdb: $(GDB) -n -x .gdbinit pre-qemu: .gdbinit +# QEMU doesn't truncate the pcap file. Work around this. + @rm -f qemu.pcap qemu: $(IMAGES) pre-qemu $(QEMU) $(QEMUOPTS) @@ -308,6 +320,7 @@ myapi.key: # @./handin-prep # For test runs +prep-net_%: override INIT_CFLAGS+=-DTEST_NO_NS prep-%: $(V)$(MAKE) "INIT_CFLAGS=${INIT_CFLAGS} -DTEST=`case $* in *_*) echo $*;; *) echo user_$*;; esac`" $(IMAGES) @@ -324,6 +337,23 @@ run-%-nox: prep-% pre-qemu run-%: prep-% pre-qemu $(QEMU) $(QEMUOPTS) +# For network connections +which-ports: + @echo "Local port $(PORT7) forwards to JOS port 7 (echo server)" + @echo "Local port $(PORT80) forwards to JOS port 80 (web server)" + +nc-80: + nc localhost $(PORT80) + +nc-7: + nc localhost $(PORT7) + +telnet-80: + telnet localhost $(PORT80) + +telnet-7: + telnet localhost $(PORT7) + # This magic automatically generates makefile dependencies # for header files included from C source files we compile, # and keeps those dependencies up-to-date every time we recompile. diff --git a/lab/Untitled Project.si4project/Backup/bc(1629).c b/lab/Untitled Project.si4project/Backup/bc(1629).c deleted file mode 100644 index e3922c4..0000000 --- a/lab/Untitled Project.si4project/Backup/bc(1629).c +++ /dev/null @@ -1,151 +0,0 @@ - -#include "fs.h" - -// Return the virtual address of this disk block. -void* -diskaddr(uint32_t blockno) -{ - if (blockno == 0 || (super && blockno >= super->s_nblocks)) - panic("bad block number %08x in diskaddr", blockno); - return (char*) (DISKMAP + blockno * BLKSIZE); -} - -// Is this virtual address mapped? -bool -va_is_mapped(void *va) -{ - return (uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P); -} - -// Is this virtual address dirty? -bool -va_is_dirty(void *va) -{ - return (uvpt[PGNUM(va)] & PTE_D) != 0; -} - -// Fault any disk block that is read in to memory by -// loading it from disk. -static void -bc_pgfault(struct UTrapframe *utf) -{ - void *addr = (void *) utf->utf_fault_va; - uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE; - int r; - - // Check that the fault was within the block cache region - if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) - panic("page fault in FS: eip %08x, va %08x, err %04x", - utf->utf_eip, addr, utf->utf_err); - - // Sanity check the block number. - if (super && blockno >= super->s_nblocks) - panic("reading non-existent block %08x\n", blockno); - - // Allocate a page in the disk map region, read the contents - // of the block from the disk into that page. - // Hint: first round addr to page boundary. fs/ide.c has code to read - // the disk. - // - // LAB 5: you code here: - - // Clear the dirty bit for the disk block page since we just read the - // block from disk - if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0) - panic("in bc_pgfault, sys_page_map: %e", r); - - // Check that the block we read was allocated. (exercise for - // the reader: why do we do this *after* reading the block - // in?) - if (bitmap && block_is_free(blockno)) - panic("reading free block %08x\n", blockno); -} - -// Flush the contents of the block containing VA out to disk if -// necessary, then clear the PTE_D bit using sys_page_map. -// If the block is not in the block cache or is not dirty, does -// nothing. -// Hint: Use va_is_mapped, va_is_dirty, and ide_write. -// Hint: Use the PTE_SYSCALL constant when calling sys_page_map. -// Hint: Don't forget to round addr down. -void -flush_block(void *addr) -{ - uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE; - - if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) - panic("flush_block of bad va %08x", addr); - - // LAB 5: Your code here. - panic("flush_block not implemented"); -} - -// Test that the block cache works, by smashing the superblock and -// reading it back. -static void -check_bc(void) -{ - struct Super backup; - - // back up super block - memmove(&backup, diskaddr(1), sizeof backup); - - // smash it - strcpy(diskaddr(1), "OOPS!\n"); - flush_block(diskaddr(1)); - assert(va_is_mapped(diskaddr(1))); - assert(!va_is_dirty(diskaddr(1))); - - // clear it out - sys_page_unmap(0, diskaddr(1)); - assert(!va_is_mapped(diskaddr(1))); - - // read it back in - assert(strcmp(diskaddr(1), "OOPS!\n") == 0); - - // fix it - memmove(diskaddr(1), &backup, sizeof backup); - flush_block(diskaddr(1)); - - // Now repeat the same experiment, but pass an unaligned address to - // flush_block. - - // back up super block - memmove(&backup, diskaddr(1), sizeof backup); - - // smash it - strcpy(diskaddr(1), "OOPS!\n"); - - // Pass an unaligned address to flush_block. - flush_block(diskaddr(1) + 20); - assert(va_is_mapped(diskaddr(1))); - - // Skip the !va_is_dirty() check because it makes the bug somewhat - // obscure and hence harder to debug. - //assert(!va_is_dirty(diskaddr(1))); - - // clear it out - sys_page_unmap(0, diskaddr(1)); - assert(!va_is_mapped(diskaddr(1))); - - // read it back in - assert(strcmp(diskaddr(1), "OOPS!\n") == 0); - - // fix it - memmove(diskaddr(1), &backup, sizeof backup); - flush_block(diskaddr(1)); - - cprintf("block cache is good\n"); -} - -void -bc_init(void) -{ - struct Super super; - set_pgfault_handler(bc_pgfault); - check_bc(); - - // cache the super block by reading it once - memmove(&super, diskaddr(1), sizeof super); -} - diff --git a/lab/Untitled Project.si4project/Backup/bc(3621).c b/lab/Untitled Project.si4project/Backup/bc(3621).c deleted file mode 100644 index dd5f20d..0000000 --- a/lab/Untitled Project.si4project/Backup/bc(3621).c +++ /dev/null @@ -1,169 +0,0 @@ - -#include "fs.h" - -// Return the virtual address of this disk block. -void* -diskaddr(uint32_t blockno) -{ - if (blockno == 0 || (super && blockno >= super->s_nblocks)) - panic("bad block number %08x in diskaddr", blockno); - return (char*) (DISKMAP + blockno * BLKSIZE); -} - -// Is this virtual address mapped? -bool -va_is_mapped(void *va) -{ - return (uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P); -} - -// Is this virtual address dirty? -bool -va_is_dirty(void *va) -{ - return (uvpt[PGNUM(va)] & PTE_D) != 0; -} - -// Fault any disk block that is read in to memory by -// loading it from disk. -static void -bc_pgfault(struct UTrapframe *utf) -{ - void *addr = (void *) utf->utf_fault_va; - uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE; - int r; - - // Check that the fault was within the block cache region - if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) - panic("page fault in FS: eip %08x, va %08x, err %04x", - utf->utf_eip, addr, utf->utf_err); - - // Sanity check the block number. - if (super && blockno >= super->s_nblocks) - panic("reading non-existent block %08x\n", blockno); - - // Allocate a page in the disk map region, read the contents - // of the block from the disk into that page. - // Hint: first round addr to page boundary. fs/ide.c has code to read - // the disk. - // - // LAB 5: you code here: - // envid 传入 0? 在最初的哪个进程下 alloc 一个page ? - addr =(void *) ROUNDDOWN(addr, PGSIZE); - if ( (r = sys_page_alloc(0, addr, PTE_P |PTE_W|PTE_U)) < 0) { - panic("in bc_pgfault, sys_page_alloc: %e", r); - } - // size_t secno = (addr - DISKMAP) / BLKSIZE; - if ( (r = ide_read(blockno*BLKSECTS, addr, BLKSECTS)) < 0) { - panic("in bc_pgfault, ide_read: %e",r); - } - - // Clear the dirty bit for the disk block page since we just read the - // block from disk - // 只是为了修改标志位 - if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0) - panic("in bc_pgfault, sys_page_map: %e", r); - - // Check that the block we read was allocated. (exercise for - // the reader: why do we do this *after* reading the block - // in?) - if (bitmap && block_is_free(blockno)) - panic("reading free block %08x\n", blockno); -} - -// Flush the contents of the block containing VA out to disk if -// necessary, then clear the PTE_D bit using sys_page_map. -// If the block is not in the block cache or is not dirty, does -// nothing. -// Hint: Use va_is_mapped, va_is_dirty, and ide_write. -// Hint: Use the PTE_SYSCALL constant when calling sys_page_map. -// Hint: Don't forget to round addr down. -void -flush_block(void *addr) -{ - uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE; - - if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) - panic("flush_block of bad va %08x", addr); - int r; - // LAB 5: Your code here. - addr = (void *)ROUNDDOWN(addr, PGSIZE); - if (va_is_mapped(addr) && va_is_dirty(addr)) { - - ide_write(blockno*BLKSECTS, addr , BLKSECTS); - if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0) - panic("in flush_block, sys_page_map: %e", r); - } - - // panic("flush_block not implemented"); -} - -// Test that the block cache works, by smashing the superblock and -// reading it back. -static void -check_bc(void) -{ - struct Super backup; - - // back up super block - memmove(&backup, diskaddr(1), sizeof backup); - - // smash it - strcpy(diskaddr(1), "OOPS!\n"); - flush_block(diskaddr(1)); - assert(va_is_mapped(diskaddr(1))); - assert(!va_is_dirty(diskaddr(1))); - - // clear it out - sys_page_unmap(0, diskaddr(1)); - assert(!va_is_mapped(diskaddr(1))); - - // read it back in - assert(strcmp(diskaddr(1), "OOPS!\n") == 0); - - // fix it - memmove(diskaddr(1), &backup, sizeof backup); - flush_block(diskaddr(1)); - - // Now repeat the same experiment, but pass an unaligned address to - // flush_block. - - // back up super block - memmove(&backup, diskaddr(1), sizeof backup); - - // smash it - strcpy(diskaddr(1), "OOPS!\n"); - - // Pass an unaligned address to flush_block. - flush_block(diskaddr(1) + 20); - assert(va_is_mapped(diskaddr(1))); - - // Skip the !va_is_dirty() check because it makes the bug somewhat - // obscure and hence harder to debug. - //assert(!va_is_dirty(diskaddr(1))); - - // clear it out - sys_page_unmap(0, diskaddr(1)); - assert(!va_is_mapped(diskaddr(1))); - - // read it back in - assert(strcmp(diskaddr(1), "OOPS!\n") == 0); - - // fix it - memmove(diskaddr(1), &backup, sizeof backup); - flush_block(diskaddr(1)); - - cprintf("block cache is good\n"); -} - -void -bc_init(void) -{ - struct Super super; - set_pgfault_handler(bc_pgfault); - check_bc(); - - // cache the super block by reading it once - memmove(&super, diskaddr(1), sizeof super); -} - diff --git a/lab/Untitled Project.si4project/Backup/env(3753).c b/lab/Untitled Project.si4project/Backup/env(3753).c deleted file mode 100644 index 03d9a34..0000000 --- a/lab/Untitled Project.si4project/Backup/env(3753).c +++ /dev/null @@ -1,578 +0,0 @@ -/* See COPYRIGHT for copyright information. */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct Env *envs = NULL; // All environments -static struct Env *env_free_list; // Free environment list - // (linked by Env->env_link) - -#define ENVGENSHIFT 12 // >= LOGNENV - -// Global descriptor table. -// -// Set up global descriptor table (GDT) with separate segments for -// kernel mode and user mode. Segments serve many purposes on the x86. -// We don't use any of their memory-mapping capabilities, but we need -// them to switch privilege levels. -// -// The kernel and user segments are identical except for the DPL. -// To load the SS register, the CPL must equal the DPL. Thus, -// we must duplicate the segments for the user and the kernel. -// -// In particular, the last argument to the SEG macro used in the -// definition of gdt specifies the Descriptor Privilege Level (DPL) -// of that descriptor: 0 for kernel and 3 for user. -// -struct Segdesc gdt[NCPU + 5] = -{ - // 0x0 - unused (always faults -- for trapping NULL far pointers) - SEG_NULL, - - // 0x8 - kernel code segment - [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0), - - // 0x10 - kernel data segment - [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0), - - // 0x18 - user code segment - [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3), - - // 0x20 - user data segment - [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3), - - // Per-CPU TSS descriptors (starting from GD_TSS0) are initialized - // in trap_init_percpu() - [GD_TSS0 >> 3] = SEG_NULL -}; - -struct Pseudodesc gdt_pd = { - sizeof(gdt) - 1, (unsigned long) gdt -}; - -// -// Converts an envid to an env pointer. -// If checkperm is set, the specified environment must be either the -// current environment or an immediate child of the current environment. -// -// RETURNS -// 0 on success, -E_BAD_ENV on error. -// On success, sets *env_store to the environment. -// On error, sets *env_store to NULL. -// -int -envid2env(envid_t envid, struct Env **env_store, bool checkperm) -{ - struct Env *e; - - // If envid is zero, return the current environment. - if (envid == 0) { - *env_store = curenv; - return 0; - } - - // Look up the Env structure via the index part of the envid, - // then check the env_id field in that struct Env - // to ensure that the envid is not stale - // (i.e., does not refer to a _previous_ environment - // that used the same slot in the envs[] array). - e = &envs[ENVX(envid)]; - if (e->env_status == ENV_FREE || e->env_id != envid) { - *env_store = 0; - return -E_BAD_ENV; - } - - // Check that the calling environment has legitimate permission - // to manipulate the specified environment. - // If checkperm is set, the specified environment - // must be either the current environment - // or an immediate child of the current environment. - if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) { - *env_store = 0; - return -E_BAD_ENV; - } - - *env_store = e; - return 0; -} - -// Mark all environments in 'envs' as free, set their env_ids to 0, -// and insert them into the env_free_list. -// Make sure the environments are in the free list in the same order -// they are in the envs array (i.e., so that the first call to -// env_alloc() returns envs[0]). -// -void -env_init(void) -{ - // Set up envs array - // LAB 3: Your code here. - int i; - // 确保最小的env在最前端 - for (i = NENV-1; i >= 0; --i) { - envs[i].env_id = 0; - - envs[i].env_link = env_free_list; - env_free_list = &envs[i]; - } - - // Per-CPU part of the initialization - env_init_percpu(); -} - -// Load GDT and segment descriptors. -void -env_init_percpu(void) -{ - lgdt(&gdt_pd); - // The kernel never uses GS or FS, so we leave those set to - // the user data segment. - asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3)); - asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3)); - // The kernel does use ES, DS, and SS. We'll change between - // the kernel and user data segments as needed. - asm volatile("movw %%ax,%%es" : : "a" (GD_KD)); - asm volatile("movw %%ax,%%ds" : : "a" (GD_KD)); - asm volatile("movw %%ax,%%ss" : : "a" (GD_KD)); - // Load the kernel text segment into CS. - asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT)); - // For good measure, clear the local descriptor table (LDT), - // since we don't use it. - lldt(0); -} - -// -// Initialize the kernel virtual memory layout for environment e. -// Allocate a page directory, set e->env_pgdir accordingly, -// and initialize the kernel portion of the new environment's address space. -// Do NOT (yet) map anything into the user portion -// of the environment's virtual address space. -// -// Returns 0 on success, < 0 on error. Errors include: -// -E_NO_MEM if page directory or table could not be allocated. -// 这里又为每个环境分配一个页目录,有点晕了。 -static int -env_setup_vm(struct Env *e) -{ - int i; - struct PageInfo *p = NULL; - - // Allocate a page for the page directory - if (!(p = page_alloc(ALLOC_ZERO))) - return -E_NO_MEM; - - // Now, set e->env_pgdir and initialize the page directory. - // - // Hint: - // - The VA space of all envs is identical above UTOP - // (except at UVPT, which we've set below). - // See inc/memlayout.h for permissions and layout. - // Can you use kern_pgdir as a template? Hint: Yes. - // (Make sure you got the permissions right in Lab 2.) - // - The initial VA below UTOP is empty. - // - You do not need to make any more calls to page_alloc. - // - Note: In general, pp_ref is not maintained for - // physical pages mapped only above UTOP, but env_pgdir - // is an exception -- you need to increment env_pgdir's - // pp_ref for env_free to work correctly. - // - The functions in kern/pmap.h are handy. - - - // LAB 3: Your code here. - // 申请一个页表存用户环境的页目录 - - e->env_pgdir = page2kva(p); - // why ?因为在UTOP之上都是一样的,所以可以直接把kern_pgdir的内容全部拷贝过来 - - memcpy(e->env_pgdir, kern_pgdir, PGSIZE); - p->pp_ref++; - // UVPT maps the env's own page table read-only. - // Permissions: kernel R, user R - // 但是唯独UVPT这个地方是不一样的,因为要放的是自己的页表目录 - e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U; - - return 0; -} - -// -// Allocates and initializes a new environment. -// On success, the new environment is stored in *newenv_store. -// -// Returns 0 on success, < 0 on failure. Errors include: -// -E_NO_FREE_ENV if all NENV environments are allocated -// -E_NO_MEM on memory exhaustion -// -int -env_alloc(struct Env **newenv_store, envid_t parent_id) -{ - int32_t generation; - int r; - struct Env *e; - - if (!(e = env_free_list)) - return -E_NO_FREE_ENV; - - // Allocate and set up the page directory for this environment. - if ((r = env_setup_vm(e)) < 0) - return r; - - // Generate an env_id for this environment. - generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1); - if (generation <= 0) // Don't create a negative env_id. - generation = 1 << ENVGENSHIFT; - e->env_id = generation | (e - envs); - - // Set the basic status variables. - e->env_parent_id = parent_id; - e->env_type = ENV_TYPE_USER; - e->env_status = ENV_RUNNABLE; - e->env_runs = 0; - - // Clear out all the saved register state, - // to prevent the register values - // of a prior environment inhabiting this Env structure - // from "leaking" into our new environment. - memset(&e->env_tf, 0, sizeof(e->env_tf)); - - // Set up appropriate initial values for the segment registers. - // GD_UD is the user data segment selector in the GDT, and - // GD_UT is the user text segment selector (see inc/memlayout.h). - // The low 2 bits of each segment register contains the - // Requestor Privilege Level (RPL); 3 means user mode. When - // we switch privilege levels, the hardware does various - // checks involving the RPL and the Descriptor Privilege Level - // (DPL) stored in the descriptors themselves. - e->env_tf.tf_ds = GD_UD | 3; - e->env_tf.tf_es = GD_UD | 3; - e->env_tf.tf_ss = GD_UD | 3; - e->env_tf.tf_esp = USTACKTOP; - e->env_tf.tf_cs = GD_UT | 3; - // You will set e->env_tf.tf_eip later. - - // Enable interrupts while in user mode. - // LAB 4: Your code here. - e->env_tf.tf_eflags |= FL_IF; - // Clear the page fault handler until user installs one. - e->env_pgfault_upcall = 0; - - // Also clear the IPC receiving flag. - e->env_ipc_recving = 0; - - // commit the allocation - env_free_list = e->env_link; - *newenv_store = e; - - // cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); - return 0; -} - -// -// Allocate len bytes of physical memory for environment env, -// and map it at virtual address va in the environment's address space. -// Does not zero or otherwise initialize the mapped pages in any way. -// Pages should be writable by user and kernel. -// Panic if any allocation attempt fails. -// -static void -region_alloc(struct Env *e, void *va, size_t len) -{ - // LAB 3: Your code here. - // (But only if you need it for load_icode.) - // - struct PageInfo *pp; - size_t i; - size_t pgs = ROUNDUP(len, PGSIZE)/PGSIZE; - size_t pva = ROUNDDOWN((size_t)va, PGSIZE); - - - for(i=0; i < pgs; i++) { - if (!(pp = page_alloc(ALLOC_ZERO))) { - int ex = -E_NO_MEM; - panic("region_alloc: %e", ex); - } - page_insert(e->env_pgdir, pp, (void *)pva, PTE_U|PTE_W|PTE_P); - pva += PGSIZE; - - } - // Hint: It is easier to use region_alloc if the caller can pass - // 'va' and 'len' values that are not page-aligned. - // You should round va down, and round (va + len) up. - // (Watch out for corner-cases!) -} - -// -// Set up the initial program binary, stack, and processor flags -// for a user process. -// This function is ONLY called during kernel initialization, -// before running the first user-mode environment. -// -// This function loads all loadable segments from the ELF binary image -// into the environment's user memory, starting at the appropriate -// virtual addresses indicated in the ELF program header. -// At the same time it clears to zero any portions of these segments -// that are marked in the program header as being mapped -// but not actually present in the ELF file - i.e., the program's bss section. -// -// All this is very similar to what our boot loader does, except the boot -// loader also needs to read the code from disk. Take a look at -// boot/main.c to get ideas. -// -// Finally, this function maps one page for the program's initial stack. -// -// load_icode panics if it encounters problems. -// - How might load_icode fail? What might be wrong with the given input? -// -static void -load_icode(struct Env *e, uint8_t *binary) -{ - // Hints: - // Load each program segment into virtual memory - // at the address specified in the ELF segment header. - // You should only load segments with ph->p_type == ELF_PROG_LOAD. - // Each segment's virtual address can be found in ph->p_va - // and its size in memory can be found in ph->p_memsz. - // The ph->p_filesz bytes from the ELF binary, starting at - // 'binary + ph->p_offset', should be copied to virtual address - // ph->p_va. Any remaining memory bytes should be cleared to zero. - // (The ELF header should have ph->p_filesz <= ph->p_memsz.) - // Use functions from the previous lab to allocate and map pages. - // - // All page protection bits should be user read/write for now. - // ELF segments are not necessarily page-aligned, but you can - // assume for this function that no two segments will touch - // the same virtual page. - // - // You may find a function like region_alloc useful. - // - // Loading the segments is much simpler if you can move data - // directly into the virtual addresses stored in the ELF binary. - // So which page directory should be in force during - // this function? - // - // You must also do something with the program's entry point, - // to make sure that the environment starts executing there. - // What? (See env_run() and env_pop_tf() below.) - - // LAB 3: Your code here. - // 怎么得到program的ELF地址? 就是bianry - struct Proghdr *ph, *eph; - struct Elf *elfhdr = (struct Elf *)binary; - - // is this a valid ELF? - if (elfhdr->e_magic != ELF_MAGIC) - panic("elf header's magic is not correct\n"); - - // 所有程序段 - ph = (struct Proghdr *)((uint8_t *)elfhdr + elfhdr->e_phoff); - eph = ph + elfhdr->e_phnum; - // 转换到用户页目录,用户空间映射 - lcr3(PADDR(e->env_pgdir)); - - for(; ph < eph; ph++) { - - if (ph->p_type != ELF_PROG_LOAD) - continue; - if (ph->p_filesz > ph->p_memsz) - panic("file size is great than memmory size\n"); - - region_alloc(e, (void *)ph->p_va, ph->p_memsz); - memcpy((void *)ph->p_va, binary + ph->p_offset, ph->p_filesz); - // clear bss section - memset((void *)ph->p_va + ph->p_filesz, 0, (ph->p_memsz - ph->p_filesz)); - - } - e->env_tf.tf_eip = elfhdr->e_entry; - // Now map one page for the program's initial stack - // at virtual address USTACKTOP - PGSIZE. - - // LAB 3: Your code here. - - // map the user stack - region_alloc(e, (void *)USTACKTOP-PGSIZE, PGSIZE); - lcr3(PADDR(kern_pgdir)); -} - -// -// Allocates a new env with env_alloc, loads the named elf -// binary into it with load_icode, and sets its env_type. -// This function is ONLY called during kernel initialization, -// before running the first user-mode environment. -// The new env's parent ID is set to 0. -// -void -env_create(uint8_t *binary, enum EnvType type) -{ - // LAB 3: Your code here. -<<<<<<< HEAD - - // If this is the file server (type == ENV_TYPE_FS) give it I/O privileges. - // LAB 5: Your code here. -======= - struct Env *newenv; - int ret = 0; - if ((ret = env_alloc(&newenv, 0)) < 0) { - panic("env_create: %e\n", ret); - } - newenv->env_type = type; - load_icode(newenv, binary); - ->>>>>>> lab4 -} - -// -// Frees env e and all memory it uses. -// -void -env_free(struct Env *e) -{ - pte_t *pt; - uint32_t pdeno, pteno; - physaddr_t pa; - - // If freeing the current environment, switch to kern_pgdir - // before freeing the page directory, just in case the page - // gets reused. - if (e == curenv) - lcr3(PADDR(kern_pgdir)); - - // Note the environment's demise. - // cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id); - - // Flush all mapped pages in the user portion of the address space - static_assert(UTOP % PTSIZE == 0); - for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) { - - // only look at mapped page tables - if (!(e->env_pgdir[pdeno] & PTE_P)) - continue; - - // find the pa and va of the page table - pa = PTE_ADDR(e->env_pgdir[pdeno]); - pt = (pte_t*) KADDR(pa); - - // unmap all PTEs in this page table - for (pteno = 0; pteno <= PTX(~0); pteno++) { - if (pt[pteno] & PTE_P) - page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0)); - } - - // free the page table itself - e->env_pgdir[pdeno] = 0; - page_decref(pa2page(pa)); - } - - // free the page directory - pa = PADDR(e->env_pgdir); - e->env_pgdir = 0; - page_decref(pa2page(pa)); - - // return the environment to the free list - e->env_status = ENV_FREE; - e->env_link = env_free_list; - env_free_list = e; -} - -// -// Frees environment e. -// If e was the current env, then runs a new environment (and does not return -// to the caller). -// -void -env_destroy(struct Env *e) -{ - // If e is currently running on other CPUs, we change its state to - // ENV_DYING. A zombie environment will be freed the next time - // it traps to the kernel. - if (e->env_status == ENV_RUNNING && curenv != e) { - e->env_status = ENV_DYING; - return; - } - - env_free(e); - - if (curenv == e) { - curenv = NULL; - sched_yield(); - } -} - - -// -// Restores the register values in the Trapframe with the 'iret' instruction. -// This exits the kernel and starts executing some environment's code. -// -// This function does not return. -// -void -env_pop_tf(struct Trapframe *tf) -{ - // Record the CPU we are running on for user-space debugging - curenv->env_cpunum = cpunum(); - - asm volatile( - "\tmovl %0,%%esp\n" - "\tpopal\n" - "\tpopl %%es\n" - "\tpopl %%ds\n" - "\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */ - "\tiret\n" - : : "g" (tf) : "memory"); - panic("iret failed"); /* mostly to placate the compiler */ -} - -// -// Context switch from curenv to env e. -// Note: if this is the first call to env_run, curenv is NULL. -// -// This function does not return. -// -void -env_run(struct Env *e) -{ - // Step 1: If this is a context switch (a new environment is running): - // 1. Set the current environment (if any) back to - // ENV_RUNNABLE if it is ENV_RUNNING (think about - // what other states it can be in), - // 2. Set 'curenv' to the new environment, - // 3. Set its status to ENV_RUNNING, - // 4. Update its 'env_runs' counter, - // 5. Use lcr3() to switch to its address space. - // Step 2: Use env_pop_tf() to restore the environment's - // registers and drop into user mode in the - // environment. - - // Hint: This function loads the new environment's state from - // e->env_tf. Go back through the code you wrote above - // and make sure you have set the relevant parts of - // e->env_tf to sensible values. - - // LAB 3: Your code here. - if (curenv && curenv->env_status == ENV_RUNNING) { - curenv->env_status = ENV_RUNNABLE; - } - curenv = e; - curenv->env_status = ENV_RUNNING; - curenv->env_runs++; - lcr3(PADDR(curenv->env_pgdir)); - - unlock_kernel(); - // iret退出内核, 回到用户环境执行, - // 在load_icode() 中 env_tf保存了可执行文件的eip等信息 - env_pop_tf(&(curenv->env_tf)); - - - // panic("env_run not yet implemented"); -} - diff --git a/lab/Untitled Project.si4project/Backup/env(840).c b/lab/Untitled Project.si4project/Backup/env(840).c deleted file mode 100644 index 2f03e35..0000000 --- a/lab/Untitled Project.si4project/Backup/env(840).c +++ /dev/null @@ -1,577 +0,0 @@ -/* See COPYRIGHT for copyright information. */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct Env *envs = NULL; // All environments -static struct Env *env_free_list; // Free environment list - // (linked by Env->env_link) - -#define ENVGENSHIFT 12 // >= LOGNENV - -// Global descriptor table. -// -// Set up global descriptor table (GDT) with separate segments for -// kernel mode and user mode. Segments serve many purposes on the x86. -// We don't use any of their memory-mapping capabilities, but we need -// them to switch privilege levels. -// -// The kernel and user segments are identical except for the DPL. -// To load the SS register, the CPL must equal the DPL. Thus, -// we must duplicate the segments for the user and the kernel. -// -// In particular, the last argument to the SEG macro used in the -// definition of gdt specifies the Descriptor Privilege Level (DPL) -// of that descriptor: 0 for kernel and 3 for user. -// -struct Segdesc gdt[NCPU + 5] = -{ - // 0x0 - unused (always faults -- for trapping NULL far pointers) - SEG_NULL, - - // 0x8 - kernel code segment - [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0), - - // 0x10 - kernel data segment - [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0), - - // 0x18 - user code segment - [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3), - - // 0x20 - user data segment - [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3), - - // Per-CPU TSS descriptors (starting from GD_TSS0) are initialized - // in trap_init_percpu() - [GD_TSS0 >> 3] = SEG_NULL -}; - -struct Pseudodesc gdt_pd = { - sizeof(gdt) - 1, (unsigned long) gdt -}; - -// -// Converts an envid to an env pointer. -// If checkperm is set, the specified environment must be either the -// current environment or an immediate child of the current environment. -// -// RETURNS -// 0 on success, -E_BAD_ENV on error. -// On success, sets *env_store to the environment. -// On error, sets *env_store to NULL. -// -int -envid2env(envid_t envid, struct Env **env_store, bool checkperm) -{ - struct Env *e; - - // If envid is zero, return the current environment. - if (envid == 0) { - *env_store = curenv; - return 0; - } - - // Look up the Env structure via the index part of the envid, - // then check the env_id field in that struct Env - // to ensure that the envid is not stale - // (i.e., does not refer to a _previous_ environment - // that used the same slot in the envs[] array). - e = &envs[ENVX(envid)]; - if (e->env_status == ENV_FREE || e->env_id != envid) { - *env_store = 0; - return -E_BAD_ENV; - } - - // Check that the calling environment has legitimate permission - // to manipulate the specified environment. - // If checkperm is set, the specified environment - // must be either the current environment - // or an immediate child of the current environment. - if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) { - *env_store = 0; - return -E_BAD_ENV; - } - - *env_store = e; - return 0; -} - -// Mark all environments in 'envs' as free, set their env_ids to 0, -// and insert them into the env_free_list. -// Make sure the environments are in the free list in the same order -// they are in the envs array (i.e., so that the first call to -// env_alloc() returns envs[0]). -// -void -env_init(void) -{ - // Set up envs array - // LAB 3: Your code here. - int i; - // 确保最小的env在最前端 - for (i = NENV-1; i >= 0; --i) { - envs[i].env_id = 0; - - envs[i].env_link = env_free_list; - env_free_list = &envs[i]; - } - - // Per-CPU part of the initialization - env_init_percpu(); -} - -// Load GDT and segment descriptors. -void -env_init_percpu(void) -{ - lgdt(&gdt_pd); - // The kernel never uses GS or FS, so we leave those set to - // the user data segment. - asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3)); - asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3)); - // The kernel does use ES, DS, and SS. We'll change between - // the kernel and user data segments as needed. - asm volatile("movw %%ax,%%es" : : "a" (GD_KD)); - asm volatile("movw %%ax,%%ds" : : "a" (GD_KD)); - asm volatile("movw %%ax,%%ss" : : "a" (GD_KD)); - // Load the kernel text segment into CS. - asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT)); - // For good measure, clear the local descriptor table (LDT), - // since we don't use it. - lldt(0); -} - -// -// Initialize the kernel virtual memory layout for environment e. -// Allocate a page directory, set e->env_pgdir accordingly, -// and initialize the kernel portion of the new environment's address space. -// Do NOT (yet) map anything into the user portion -// of the environment's virtual address space. -// -// Returns 0 on success, < 0 on error. Errors include: -// -E_NO_MEM if page directory or table could not be allocated. -// 这里又为每个环境分配一个页目录,有点晕了。 -static int -env_setup_vm(struct Env *e) -{ - int i; - struct PageInfo *p = NULL; - - // Allocate a page for the page directory - if (!(p = page_alloc(ALLOC_ZERO))) - return -E_NO_MEM; - - // Now, set e->env_pgdir and initialize the page directory. - // - // Hint: - // - The VA space of all envs is identical above UTOP - // (except at UVPT, which we've set below). - // See inc/memlayout.h for permissions and layout. - // Can you use kern_pgdir as a template? Hint: Yes. - // (Make sure you got the permissions right in Lab 2.) - // - The initial VA below UTOP is empty. - // - You do not need to make any more calls to page_alloc. - // - Note: In general, pp_ref is not maintained for - // physical pages mapped only above UTOP, but env_pgdir - // is an exception -- you need to increment env_pgdir's - // pp_ref for env_free to work correctly. - // - The functions in kern/pmap.h are handy. - - - // LAB 3: Your code here. - // 申请一个页表存用户环境的页目录 - - e->env_pgdir = page2kva(p); - // why ?因为在UTOP之上都是一样的,所以可以直接把kern_pgdir的内容全部拷贝过来 - - memcpy(e->env_pgdir, kern_pgdir, PGSIZE); - p->pp_ref++; - // UVPT maps the env's own page table read-only. - // Permissions: kernel R, user R - // 但是唯独UVPT这个地方是不一样的,因为要放的是自己的页表目录 - e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U; - - return 0; -} - -// -// Allocates and initializes a new environment. -// On success, the new environment is stored in *newenv_store. -// -// Returns 0 on success, < 0 on failure. Errors include: -// -E_NO_FREE_ENV if all NENV environments are allocated -// -E_NO_MEM on memory exhaustion -// -int -env_alloc(struct Env **newenv_store, envid_t parent_id) -{ - int32_t generation; - int r; - struct Env *e; - - if (!(e = env_free_list)) - return -E_NO_FREE_ENV; - - // Allocate and set up the page directory for this environment. - if ((r = env_setup_vm(e)) < 0) - return r; - - // Generate an env_id for this environment. - generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1); - if (generation <= 0) // Don't create a negative env_id. - generation = 1 << ENVGENSHIFT; - e->env_id = generation | (e - envs); - - // Set the basic status variables. - e->env_parent_id = parent_id; - e->env_type = ENV_TYPE_USER; - e->env_status = ENV_RUNNABLE; - e->env_runs = 0; - - // Clear out all the saved register state, - // to prevent the register values - // of a prior environment inhabiting this Env structure - // from "leaking" into our new environment. - memset(&e->env_tf, 0, sizeof(e->env_tf)); - - // Set up appropriate initial values for the segment registers. - // GD_UD is the user data segment selector in the GDT, and - // GD_UT is the user text segment selector (see inc/memlayout.h). - // The low 2 bits of each segment register contains the - // Requestor Privilege Level (RPL); 3 means user mode. When - // we switch privilege levels, the hardware does various - // checks involving the RPL and the Descriptor Privilege Level - // (DPL) stored in the descriptors themselves. - e->env_tf.tf_ds = GD_UD | 3; - e->env_tf.tf_es = GD_UD | 3; - e->env_tf.tf_ss = GD_UD | 3; - e->env_tf.tf_esp = USTACKTOP; - e->env_tf.tf_cs = GD_UT | 3; - // You will set e->env_tf.tf_eip later. - - // Enable interrupts while in user mode. - // LAB 4: Your code here. - e->env_tf.tf_eflags |= FL_IF; - // Clear the page fault handler until user installs one. - e->env_pgfault_upcall = 0; - - // Also clear the IPC receiving flag. - e->env_ipc_recving = 0; - - // commit the allocation - env_free_list = e->env_link; - *newenv_store = e; - - cprintf(".%08x. new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); - return 0; -} - -// -// Allocate len bytes of physical memory for environment env, -// and map it at virtual address va in the environment's address space. -// Does not zero or otherwise initialize the mapped pages in any way. -// Pages should be writable by user and kernel. -// Panic if any allocation attempt fails. -// -static void -region_alloc(struct Env *e, void *va, size_t len) -{ - // LAB 3: Your code here. - // (But only if you need it for load_icode.) - // - struct PageInfo *pp; - size_t i; - size_t pgs = ROUNDUP(len, PGSIZE)/PGSIZE; - size_t pva = ROUNDDOWN((size_t)va, PGSIZE); - - - for(i=0; i < pgs; i++) { - if (!(pp = page_alloc(ALLOC_ZERO))) { - int ex = -E_NO_MEM; - panic("region_alloc: %e", ex); - } - page_insert(e->env_pgdir, pp, (void *)pva, PTE_U|PTE_W|PTE_P); - pva += PGSIZE; - - } - // Hint: It is easier to use region_alloc if the caller can pass - // 'va' and 'len' values that are not page-aligned. - // You should round va down, and round (va + len) up. - // (Watch out for corner-cases!) -} - -// -// Set up the initial program binary, stack, and processor flags -// for a user process. -// This function is ONLY called during kernel initialization, -// before running the first user-mode environment. -// -// This function loads all loadable segments from the ELF binary image -// into the environment's user memory, starting at the appropriate -// virtual addresses indicated in the ELF program header. -// At the same time it clears to zero any portions of these segments -// that are marked in the program header as being mapped -// but not actually present in the ELF file - i.e., the program's bss section. -// -// All this is very similar to what our boot loader does, except the boot -// loader also needs to read the code from disk. Take a look at -// boot/main.c to get ideas. -// -// Finally, this function maps one page for the program's initial stack. -// -// load_icode panics if it encounters problems. -// - How might load_icode fail? What might be wrong with the given input? -// -static void -load_icode(struct Env *e, uint8_t *binary) -{ - // Hints: - // Load each program segment into virtual memory - // at the address specified in the ELF segment header. - // You should only load segments with ph->p_type == ELF_PROG_LOAD. - // Each segment's virtual address can be found in ph->p_va - // and its size in memory can be found in ph->p_memsz. - // The ph->p_filesz bytes from the ELF binary, starting at - // 'binary + ph->p_offset', should be copied to virtual address - // ph->p_va. Any remaining memory bytes should be cleared to zero. - // (The ELF header should have ph->p_filesz <= ph->p_memsz.) - // Use functions from the previous lab to allocate and map pages. - // - // All page protection bits should be user read/write for now. - // ELF segments are not necessarily page-aligned, but you can - // assume for this function that no two segments will touch - // the same virtual page. - // - // You may find a function like region_alloc useful. - // - // Loading the segments is much simpler if you can move data - // directly into the virtual addresses stored in the ELF binary. - // So which page directory should be in force during - // this function? - // - // You must also do something with the program's entry point, - // to make sure that the environment starts executing there. - // What? (See env_run() and env_pop_tf() below.) - - // LAB 3: Your code here. - // 怎么得到program的ELF地址? 就是bianry - struct Proghdr *ph, *eph; - struct Elf *elfhdr = (struct Elf *)binary; - - // is this a valid ELF? - if (elfhdr->e_magic != ELF_MAGIC) - panic("elf header's magic is not correct\n"); - - // 所有程序段 - ph = (struct Proghdr *)((uint8_t *)elfhdr + elfhdr->e_phoff); - eph = ph + elfhdr->e_phnum; - // 转换到用户页目录,用户空间映射 - lcr3(PADDR(e->env_pgdir)); - - for(; ph < eph; ph++) { - - if (ph->p_type != ELF_PROG_LOAD) - continue; - if (ph->p_filesz > ph->p_memsz) - panic("file size is great than memmory size\n"); - - region_alloc(e, (void *)ph->p_va, ph->p_memsz); - memcpy((void *)ph->p_va, binary + ph->p_offset, ph->p_filesz); - // clear bss section - memset((void *)ph->p_va + ph->p_filesz, 0, (ph->p_memsz - ph->p_filesz)); - - } - e->env_tf.tf_eip = elfhdr->e_entry; - // Now map one page for the program's initial stack - // at virtual address USTACKTOP - PGSIZE. - - // LAB 3: Your code here. - - // map the user stack - region_alloc(e, (void *)USTACKTOP-PGSIZE, PGSIZE); - lcr3(PADDR(kern_pgdir)); -} - -// -// Allocates a new env with env_alloc, loads the named elf -// binary into it with load_icode, and sets its env_type. -// This function is ONLY called during kernel initialization, -// before running the first user-mode environment. -// The new env's parent ID is set to 0. -// -void -env_create(uint8_t *binary, enum EnvType type) -{ - // LAB 3: Your code here. - - struct Env *newenv; - int ret = 0; - if ((ret = env_alloc(&newenv, 0)) < 0) { - panic("env_create: %e\n", ret); - } - newenv->env_type = type; - if (type == ENV_TYPE_FS) { - newenv->env_tf.tf_eflags |= FL_IOPL_MASK; - } - load_icode(newenv, binary); - // If this is the file server (type == ENV_TYPE_FS) give it I/O privileges. - // LAB 5: Your code here. -} - -// -// Frees env e and all memory it uses. -// -void -env_free(struct Env *e) -{ - pte_t *pt; - uint32_t pdeno, pteno; - physaddr_t pa; - - // If freeing the current environment, switch to kern_pgdir - // before freeing the page directory, just in case the page - // gets reused. - if (e == curenv) - lcr3(PADDR(kern_pgdir)); - - // Note the environment's demise. - cprintf(".%08x. free env %08x\n", curenv ? curenv->env_id : 0, e->env_id); - - // Flush all mapped pages in the user portion of the address space - static_assert(UTOP % PTSIZE == 0); - for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) { - - // only look at mapped page tables - if (!(e->env_pgdir[pdeno] & PTE_P)) - continue; - - // find the pa and va of the page table - pa = PTE_ADDR(e->env_pgdir[pdeno]); - pt = (pte_t*) KADDR(pa); - - // unmap all PTEs in this page table - for (pteno = 0; pteno <= PTX(~0); pteno++) { - if (pt[pteno] & PTE_P) - page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0)); - } - - // free the page table itself - e->env_pgdir[pdeno] = 0; - page_decref(pa2page(pa)); - } - - // free the page directory - pa = PADDR(e->env_pgdir); - e->env_pgdir = 0; - page_decref(pa2page(pa)); - - // return the environment to the free list - e->env_status = ENV_FREE; - e->env_link = env_free_list; - env_free_list = e; -} - -// -// Frees environment e. -// If e was the current env, then runs a new environment (and does not return -// to the caller). -// -void -env_destroy(struct Env *e) -{ - // If e is currently running on other CPUs, we change its state to - // ENV_DYING. A zombie environment will be freed the next time - // it traps to the kernel. - if (e->env_status == ENV_RUNNING && curenv != e) { - e->env_status = ENV_DYING; - return; - } - - env_free(e); - - if (curenv == e) { - curenv = NULL; - sched_yield(); - } -} - - -// -// Restores the register values in the Trapframe with the 'iret' instruction. -// This exits the kernel and starts executing some environment's code. -// -// This function does not return. -// -void -env_pop_tf(struct Trapframe *tf) -{ - // Record the CPU we are running on for user-space debugging - curenv->env_cpunum = cpunum(); - - asm volatile( - "\tmovl %0,%%esp\n" - "\tpopal\n" - "\tpopl %%es\n" - "\tpopl %%ds\n" - "\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */ - "\tiret\n" - : : "g" (tf) : "memory"); - panic("iret failed"); /* mostly to placate the compiler */ -} - -// -// Context switch from curenv to env e. -// Note: if this is the first call to env_run, curenv is NULL. -// -// This function does not return. -// -void -env_run(struct Env *e) -{ - // Step 1: If this is a context switch (a new environment is running): - // 1. Set the current environment (if any) back to - // ENV_RUNNABLE if it is ENV_RUNNING (think about - // what other states it can be in), - // 2. Set 'curenv' to the new environment, - // 3. Set its status to ENV_RUNNING, - // 4. Update its 'env_runs' counter, - // 5. Use lcr3() to switch to its address space. - // Step 2: Use env_pop_tf() to restore the environment's - // registers and drop into user mode in the - // environment. - - // Hint: This function loads the new environment's state from - // e->env_tf. Go back through the code you wrote above - // and make sure you have set the relevant parts of - // e->env_tf to sensible values. - - // LAB 3: Your code here. - if (curenv && curenv->env_status == ENV_RUNNING) { - curenv->env_status = ENV_RUNNABLE; - } - curenv = e; - curenv->env_status = ENV_RUNNING; - curenv->env_runs++; - lcr3(PADDR(curenv->env_pgdir)); - - unlock_kernel(); - // iret退出内核, 回到用户环境执行, - // 在load_icode() 中 env_tf保存了可执行文件的eip等信息 - env_pop_tf(&(curenv->env_tf)); - - - // panic("env_run not yet implemented"); -} - diff --git a/lab/Untitled Project.si4project/Backup/exit(6898).c b/lab/Untitled Project.si4project/Backup/exit(6898).c deleted file mode 100644 index cee3336..0000000 --- a/lab/Untitled Project.si4project/Backup/exit(6898).c +++ /dev/null @@ -1,10 +0,0 @@ - -#include - -void -exit(void) -{ - close_all(); - sys_env_destroy(0); -} - diff --git a/lab/Untitled Project.si4project/Backup/faultio(2032).c b/lab/Untitled Project.si4project/Backup/faultio(2032).c deleted file mode 100644 index 4308a65..0000000 --- a/lab/Untitled Project.si4project/Backup/faultio(2032).c +++ /dev/null @@ -1,22 +0,0 @@ -// test user-level fault handler -- alloc pages to fix faults - -#include -#include - -void -umain(int argc, char **argv) -{ - int x, r; - int nsecs = 1; - int secno = 0; - int diskno = 1; - - if (read_eflags() & FL_IOPL_3) - cprintf("eflags wrong\n"); - - // this outb to select disk 1 should result in a general protection - // fault, because user-level code shouldn't be able to use the io space. - outb(0x1F6, 0xE0 | (1<<4)); - - cprintf("%s: made it here --- bug\n"); -} diff --git a/lab/Untitled Project.si4project/Backup/file(8133).c b/lab/Untitled Project.si4project/Backup/file(8133).c deleted file mode 100644 index 39025b2..0000000 --- a/lab/Untitled Project.si4project/Backup/file(8133).c +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include -#include - -#define debug 0 - -union Fsipc fsipcbuf __attribute__((aligned(PGSIZE))); - -// Send an inter-environment request to the file server, and wait for -// a reply. The request body should be in fsipcbuf, and parts of the -// response may be written back to fsipcbuf. -// type: request code, passed as the simple integer IPC value. -// dstva: virtual address at which to receive reply page, 0 if none. -// Returns result from the file server. -static int -fsipc(unsigned type, void *dstva) -{ - static envid_t fsenv; - if (fsenv == 0) - fsenv = ipc_find_env(ENV_TYPE_FS); - - static_assert(sizeof(fsipcbuf) == PGSIZE); - - if (debug) - cprintf("[%08x] fsipc %d %08x\n", thisenv->env_id, type, *(uint32_t *)&fsipcbuf); - - ipc_send(fsenv, type, &fsipcbuf, PTE_P | PTE_W | PTE_U); - return ipc_recv(NULL, dstva, NULL); -} - -static int devfile_flush(struct Fd *fd); -static ssize_t devfile_read(struct Fd *fd, void *buf, size_t n); -static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n); -static int devfile_stat(struct Fd *fd, struct Stat *stat); -static int devfile_trunc(struct Fd *fd, off_t newsize); - -struct Dev devfile = -{ - .dev_id = 'f', - .dev_name = "file", - .dev_read = devfile_read, - .dev_close = devfile_flush, - .dev_stat = devfile_stat, - .dev_write = devfile_write, - .dev_trunc = devfile_trunc -}; - -// Open a file (or directory). -// -// Returns: -// The file descriptor index on success -// -E_BAD_PATH if the path is too long (>= MAXPATHLEN) -// < 0 for other errors. -int -open(const char *path, int mode) -{ - // Find an unused file descriptor page using fd_alloc. - // Then send a file-open request to the file server. - // Include 'path' and 'omode' in request, - // and map the returned file descriptor page - // at the appropriate fd address. - // FSREQ_OPEN returns 0 on success, < 0 on failure. - // - // (fd_alloc does not allocate a page, it just returns an - // unused fd address. Do you need to allocate a page?) - // - // Return the file descriptor index. - // If any step after fd_alloc fails, use fd_close to free the - // file descriptor. - - int r; - struct Fd *fd; - - if (strlen(path) >= MAXPATHLEN) - return -E_BAD_PATH; - - if ((r = fd_alloc(&fd)) < 0) - return r; - - strcpy(fsipcbuf.open.req_path, path); - fsipcbuf.open.req_omode = mode; - - if ((r = fsipc(FSREQ_OPEN, fd)) < 0) { - fd_close(fd, 0); - return r; - } - - return fd2num(fd); -} - -// Flush the file descriptor. After this the fileid is invalid. -// -// This function is called by fd_close. fd_close will take care of -// unmapping the FD page from this environment. Since the server uses -// the reference counts on the FD pages to detect which files are -// open, unmapping it is enough to free up server-side resources. -// Other than that, we just have to make sure our changes are flushed -// to disk. -static int -devfile_flush(struct Fd *fd) -{ - fsipcbuf.flush.req_fileid = fd->fd_file.id; - return fsipc(FSREQ_FLUSH, NULL); -} - -// Read at most 'n' bytes from 'fd' at the current position into 'buf'. -// -// Returns: -// The number of bytes successfully read. -// < 0 on error. -static ssize_t -devfile_read(struct Fd *fd, void *buf, size_t n) -{ - // Make an FSREQ_READ request to the file system server after - // filling fsipcbuf.read with the request arguments. The - // bytes read will be written back to fsipcbuf by the file - // system server. - int r; - - fsipcbuf.read.req_fileid = fd->fd_file.id; - fsipcbuf.read.req_n = n; - if ((r = fsipc(FSREQ_READ, NULL)) < 0) - return r; - assert(r <= n); - assert(r <= PGSIZE); - memmove(buf, fsipcbuf.readRet.ret_buf, r); - return r; -} - - -// Write at most 'n' bytes from 'buf' to 'fd' at the current seek position. -// -// Returns: -// The number of bytes successfully written. -// < 0 on error. -static ssize_t -devfile_write(struct Fd *fd, const void *buf, size_t n) -{ - // Make an FSREQ_WRITE request to the file system server. Be - // careful: fsipcbuf.write.req_buf is only so large, but - // remember that write is always allowed to write *fewer* - // bytes than requested. - // LAB 5: Your code here - panic("devfile_write not implemented"); -} - -static int -devfile_stat(struct Fd *fd, struct Stat *st) -{ - int r; - - fsipcbuf.stat.req_fileid = fd->fd_file.id; - if ((r = fsipc(FSREQ_STAT, NULL)) < 0) - return r; - strcpy(st->st_name, fsipcbuf.statRet.ret_name); - st->st_size = fsipcbuf.statRet.ret_size; - st->st_isdir = fsipcbuf.statRet.ret_isdir; - return 0; -} - -// Truncate or extend an open file to 'size' bytes -static int -devfile_trunc(struct Fd *fd, off_t newsize) -{ - fsipcbuf.set_size.req_fileid = fd->fd_file.id; - fsipcbuf.set_size.req_size = newsize; - return fsipc(FSREQ_SET_SIZE, NULL); -} - - -// Synchronize disk with buffer cache -int -sync(void) -{ - // Ask the file server to update the disk - // by writing any dirty blocks in the buffer cache. - - return fsipc(FSREQ_SYNC, NULL); -} - diff --git a/lab/Untitled Project.si4project/Backup/fork(6291).c b/lab/Untitled Project.si4project/Backup/fork(6291).c deleted file mode 100644 index 644e0c9..0000000 --- a/lab/Untitled Project.si4project/Backup/fork(6291).c +++ /dev/null @@ -1,167 +0,0 @@ -// implement fork from user space - -#include -#include - -// PTE_COW marks copy-on-write page table entries. -// It is one of the bits explicitly allocated to user processes (PTE_AVAIL). -#define PTE_COW 0x800 - -// -// Custom page fault handler - if faulting page is copy-on-write, -// map in our own private writable copy. -// -static void -pgfault(struct UTrapframe *utf) -{ - void *addr = (void *) utf->utf_fault_va; - uint32_t err = utf->utf_err; - int r; - - // Check that the faulting access was (1) a write, and (2) to a - // copy-on-write page. If not, panic. - // Hint: - // Use the read-only page table mappings at uvpt - // (see ). - - // LAB 4: Your code here. - if (! ( (err & FEC_WR) && (uvpd[PDX(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_COW))) - panic("Neither the fault is a write nor COW page. \n"); - // Allocate a new page, map it at a temporary location (PFTEMP), - // copy the data from the old page to the new page, then move the new - // page to the old page's address. - // Hint: - // You should make three system calls. - - // LAB 4: Your code here. - envid_t envid = sys_getenvid(); - // cprintf("pgfault: envid: %d\n", ENVX(envid)); - // 临时页暂存 - if ((r = sys_page_alloc(envid, (void *)PFTEMP, PTE_P| PTE_W|PTE_U)) < 0) - panic("pgfault: page allocation fault:%e\n", r); - addr = ROUNDDOWN(addr, PGSIZE); - memcpy((void *) PFTEMP, (const void *) addr, PGSIZE); - if ((r = sys_page_map(envid, (void *) PFTEMP, envid, addr , PTE_P|PTE_W|PTE_U)) < 0 ) - panic("pgfault: page map failed %e\n", r); - - if ((r = sys_page_unmap(envid, (void *) PFTEMP)) < 0) - panic("pgfault: page unmap failed %e\n", r); - - - - // panic("pgfault not implemented"); -} - -// -// Map our virtual page pn (address pn*PGSIZE) into the target envid -// at the same virtual address. If the page is writable or copy-on-write, -// the new mapping must be created copy-on-write, and then our mapping must be -// marked copy-on-write as well. (Exercise: Why do we need to mark ours -// copy-on-write again if it was already copy-on-write at the beginning of -// this function?) -// -// Returns: 0 on success, < 0 on error. -// It is also OK to panic on error. -// - -static int -duppage(envid_t envid, unsigned pn) -{ - - // LAB 4: Your code here. - pte_t *pte; - int ret; - // 用户空间的地址较低 - uint32_t va = pn * PGSIZE; - - - if ( (uvpt[pn] & PTE_W) || (uvpt[pn] & PTE_COW)) { - - // 子进程标记 - if ((ret = sys_page_map(thisenv->env_id, (void *) va, envid, (void *) va, PTE_P|PTE_U|PTE_COW)) < 0) - return ret; - // 父进程标记 - if ((ret = sys_page_map(thisenv->env_id, (void *)va, thisenv->env_id, (void *)va, PTE_P|PTE_U|PTE_COW)) < 0) - return ret; - } - else { - // 简单映射 - if((ret = sys_page_map(thisenv->env_id, (void *) va, envid, (void * )va, PTE_P|PTE_U)) <0 ) - return ret; - } - - return 0; - // panic("duppage not implemented"); -} - - -// -// User-level fork with copy-on-write. -// Set up our page fault handler appropriately. -// Create a child. -// Copy our address space and page fault handler setup to the child. -// Then mark the child as runnable and return. -// -// Returns: child's envid to the parent, 0 to the child, < 0 on error. -// It is also OK to panic on error. -// -// Hint: -// Use uvpd, uvpt, and duppage. -// Remember to fix "thisenv" in the child process. -// Neither user exception stack should ever be marked copy-on-write, -// so you must allocate a new page for the child's user exception stack. -// -envid_t -fork(void) -{ - // LAB 4: Your code here. - envid_t envid; - int r; - size_t i, j, pn; - // Set up our page fault handler - set_pgfault_handler(pgfault); - - envid = sys_exofork(); - - if (envid < 0) { - panic("sys_exofork failed: %e", envid); - } - - if (envid == 0) { - // child - thisenv = &envs[ENVX(sys_getenvid())]; - return 0; - } - // here is parent ! - // Copy our address space and page fault handler setup to the child. - - for (pn = PGNUM(UTEXT); pn < PGNUM(USTACKTOP); pn++) { - if ( (uvpd[pn >> 10] & PTE_P) && (uvpt[pn] & PTE_P)) { - // 页表 - if ( (r = duppage(envid, pn)) < 0) - return r; - - } - } - // alloc a page and map child exception stack - if ((r = sys_page_alloc(envid, (void *)(UXSTACKTOP-PGSIZE), PTE_U | PTE_P | PTE_W)) < 0) - return r; - extern void _pgfault_upcall(void); - if ((r = sys_env_set_pgfault_upcall(envid, _pgfault_upcall)) < 0) - return r; - - // Start the child environment running - if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) - panic("sys_env_set_status: %e", r); - - return envid; - // panic("fork not implemented"); -} - -// Challenge! -int -sfork(void) -{ - panic("sfork not implemented"); - return -E_INVAL; -} diff --git a/lab/Untitled Project.si4project/Backup/fs(6931).c b/lab/Untitled Project.si4project/Backup/fs(6931).c deleted file mode 100644 index 45ecaf8..0000000 --- a/lab/Untitled Project.si4project/Backup/fs(6931).c +++ /dev/null @@ -1,456 +0,0 @@ -#include -#include - -#include "fs.h" - -// -------------------------------------------------------------- -// Super block -// -------------------------------------------------------------- - -// Validate the file system super-block. -void -check_super(void) -{ - if (super->s_magic != FS_MAGIC) - panic("bad file system magic number"); - - if (super->s_nblocks > DISKSIZE/BLKSIZE) - panic("file system is too large"); - - cprintf("superblock is good\n"); -} - -// -------------------------------------------------------------- -// Free block bitmap -// -------------------------------------------------------------- - -// Check to see if the block bitmap indicates that block 'blockno' is free. -// Return 1 if the block is free, 0 if not. -bool -block_is_free(uint32_t blockno) -{ - if (super == 0 || blockno >= super->s_nblocks) - return 0; - if (bitmap[blockno / 32] & (1 << (blockno % 32))) - return 1; - return 0; -} - -// Mark a block free in the bitmap -void -free_block(uint32_t blockno) -{ - // Blockno zero is the null pointer of block numbers. - if (blockno == 0) - panic("attempt to free zero block"); - bitmap[blockno/32] |= 1<<(blockno%32); -} - -// Search the bitmap for a free block and allocate it. When you -// allocate a block, immediately flush the changed bitmap block -// to disk. -// -// Return block number allocated on success, -// -E_NO_DISK if we are out of blocks. -// -// Hint: use free_block as an example for manipulating the bitmap. -int -alloc_block(void) -{ - // The bitmap consists of one or more blocks. A single bitmap block - // contains the in-use bits for BLKBITSIZE blocks. There are - // super->s_nblocks blocks in the disk altogether. - - // LAB 5: Your code here. - panic("alloc_block not implemented"); - return -E_NO_DISK; -} - -// Validate the file system bitmap. -// -// Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves -- -// are all marked as in-use. -void -check_bitmap(void) -{ - uint32_t i; - - // Make sure all bitmap blocks are marked in-use - for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++) - assert(!block_is_free(2+i)); - - // Make sure the reserved and root blocks are marked in-use. - assert(!block_is_free(0)); - assert(!block_is_free(1)); - - cprintf("bitmap is good\n"); -} - -// -------------------------------------------------------------- -// File system structures -// -------------------------------------------------------------- - - - -// Initialize the file system -void -fs_init(void) -{ - static_assert(sizeof(struct File) == 256); - - // Find a JOS disk. Use the second IDE disk (number 1) if available - if (ide_probe_disk1()) - ide_set_disk(1); - else - ide_set_disk(0); - bc_init(); - - // Set "super" to point to the super block. - super = diskaddr(1); - check_super(); - - // Set "bitmap" to the beginning of the first bitmap block. - bitmap = diskaddr(2); - check_bitmap(); - -} - -// Find the disk block number slot for the 'filebno'th block in file 'f'. -// Set '*ppdiskbno' to point to that slot. -// The slot will be one of the f->f_direct[] entries, -// or an entry in the indirect block. -// When 'alloc' is set, this function will allocate an indirect block -// if necessary. -// -// Returns: -// 0 on success (but note that *ppdiskbno might equal 0). -// -E_NOT_FOUND if the function needed to allocate an indirect block, but -// alloc was 0. -// -E_NO_DISK if there's no space on the disk for an indirect block. -// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT). -// -// Analogy: This is like pgdir_walk for files. -// Hint: Don't forget to clear any block you allocate. -static int -file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc) -{ - // LAB 5: Your code here. - panic("file_block_walk not implemented"); -} - -// Set *blk to the address in memory where the filebno'th -// block of file 'f' would be mapped. -// -// Returns 0 on success, < 0 on error. Errors are: -// -E_NO_DISK if a block needed to be allocated but the disk is full. -// -E_INVAL if filebno is out of range. -// -// Hint: Use file_block_walk and alloc_block. -int -file_get_block(struct File *f, uint32_t filebno, char **blk) -{ - // LAB 5: Your code here. - panic("file_get_block not implemented"); -} - -// Try to find a file named "name" in dir. If so, set *file to it. -// -// Returns 0 and sets *file on success, < 0 on error. Errors are: -// -E_NOT_FOUND if the file is not found -static int -dir_lookup(struct File *dir, const char *name, struct File **file) -{ - int r; - uint32_t i, j, nblock; - char *blk; - struct File *f; - - // Search dir for name. - // We maintain the invariant that the size of a directory-file - // is always a multiple of the file system's block size. - assert((dir->f_size % BLKSIZE) == 0); - nblock = dir->f_size / BLKSIZE; - for (i = 0; i < nblock; i++) { - if ((r = file_get_block(dir, i, &blk)) < 0) - return r; - f = (struct File*) blk; - for (j = 0; j < BLKFILES; j++) - if (strcmp(f[j].f_name, name) == 0) { - *file = &f[j]; - return 0; - } - } - return -E_NOT_FOUND; -} - -// Set *file to point at a free File structure in dir. The caller is -// responsible for filling in the File fields. -static int -dir_alloc_file(struct File *dir, struct File **file) -{ - int r; - uint32_t nblock, i, j; - char *blk; - struct File *f; - - assert((dir->f_size % BLKSIZE) == 0); - nblock = dir->f_size / BLKSIZE; - for (i = 0; i < nblock; i++) { - if ((r = file_get_block(dir, i, &blk)) < 0) - return r; - f = (struct File*) blk; - for (j = 0; j < BLKFILES; j++) - if (f[j].f_name[0] == '\0') { - *file = &f[j]; - return 0; - } - } - dir->f_size += BLKSIZE; - if ((r = file_get_block(dir, i, &blk)) < 0) - return r; - f = (struct File*) blk; - *file = &f[0]; - return 0; -} - -// Skip over slashes. -static const char* -skip_slash(const char *p) -{ - while (*p == '/') - p++; - return p; -} - -// Evaluate a path name, starting at the root. -// On success, set *pf to the file we found -// and set *pdir to the directory the file is in. -// If we cannot find the file but find the directory -// it should be in, set *pdir and copy the final path -// element into lastelem. -static int -walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem) -{ - const char *p; - char name[MAXNAMELEN]; - struct File *dir, *f; - int r; - - // if (*path != '/') - // return -E_BAD_PATH; - path = skip_slash(path); - f = &super->s_root; - dir = 0; - name[0] = 0; - - if (pdir) - *pdir = 0; - *pf = 0; - while (*path != '\0') { - dir = f; - p = path; - while (*path != '/' && *path != '\0') - path++; - if (path - p >= MAXNAMELEN) - return -E_BAD_PATH; - memmove(name, p, path - p); - name[path - p] = '\0'; - path = skip_slash(path); - - if (dir->f_type != FTYPE_DIR) - return -E_NOT_FOUND; - - if ((r = dir_lookup(dir, name, &f)) < 0) { - if (r == -E_NOT_FOUND && *path == '\0') { - if (pdir) - *pdir = dir; - if (lastelem) - strcpy(lastelem, name); - *pf = 0; - } - return r; - } - } - - if (pdir) - *pdir = dir; - *pf = f; - return 0; -} - -// -------------------------------------------------------------- -// File operations -// -------------------------------------------------------------- - -// Create "path". On success set *pf to point at the file and return 0. -// On error return < 0. -int -file_create(const char *path, struct File **pf) -{ - char name[MAXNAMELEN]; - int r; - struct File *dir, *f; - - if ((r = walk_path(path, &dir, &f, name)) == 0) - return -E_FILE_EXISTS; - if (r != -E_NOT_FOUND || dir == 0) - return r; - if ((r = dir_alloc_file(dir, &f)) < 0) - return r; - - strcpy(f->f_name, name); - *pf = f; - file_flush(dir); - return 0; -} - -// Open "path". On success set *pf to point at the file and return 0. -// On error return < 0. -int -file_open(const char *path, struct File **pf) -{ - return walk_path(path, 0, pf, 0); -} - -// Read count bytes from f into buf, starting from seek position -// offset. This meant to mimic the standard pread function. -// Returns the number of bytes read, < 0 on error. -ssize_t -file_read(struct File *f, void *buf, size_t count, off_t offset) -{ - int r, bn; - off_t pos; - char *blk; - - if (offset >= f->f_size) - return 0; - - count = MIN(count, f->f_size - offset); - - for (pos = offset; pos < offset + count; ) { - if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0) - return r; - bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos); - memmove(buf, blk + pos % BLKSIZE, bn); - pos += bn; - buf += bn; - } - - return count; -} - - -// Write count bytes from buf into f, starting at seek position -// offset. This is meant to mimic the standard pwrite function. -// Extends the file if necessary. -// Returns the number of bytes written, < 0 on error. -int -file_write(struct File *f, const void *buf, size_t count, off_t offset) -{ - int r, bn; - off_t pos; - char *blk; - - // Extend file if necessary - if (offset + count > f->f_size) - if ((r = file_set_size(f, offset + count)) < 0) - return r; - - for (pos = offset; pos < offset + count; ) { - if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0) - return r; - bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos); - memmove(blk + pos % BLKSIZE, buf, bn); - pos += bn; - buf += bn; - } - - return count; -} - -// Remove a block from file f. If it's not there, just silently succeed. -// Returns 0 on success, < 0 on error. -static int -file_free_block(struct File *f, uint32_t filebno) -{ - int r; - uint32_t *ptr; - - if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0) - return r; - if (*ptr) { - free_block(*ptr); - *ptr = 0; - } - return 0; -} - -// Remove any blocks currently used by file 'f', -// but not necessary for a file of size 'newsize'. -// For both the old and new sizes, figure out the number of blocks required, -// and then clear the blocks from new_nblocks to old_nblocks. -// If the new_nblocks is no more than NDIRECT, and the indirect block has -// been allocated (f->f_indirect != 0), then free the indirect block too. -// (Remember to clear the f->f_indirect pointer so you'll know -// whether it's valid!) -// Do not change f->f_size. -static void -file_truncate_blocks(struct File *f, off_t newsize) -{ - int r; - uint32_t bno, old_nblocks, new_nblocks; - - old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE; - new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE; - for (bno = new_nblocks; bno < old_nblocks; bno++) - if ((r = file_free_block(f, bno)) < 0) - cprintf("warning: file_free_block: %e", r); - - if (new_nblocks <= NDIRECT && f->f_indirect) { - free_block(f->f_indirect); - f->f_indirect = 0; - } -} - -// Set the size of file f, truncating or extending as necessary. -int -file_set_size(struct File *f, off_t newsize) -{ - if (f->f_size > newsize) - file_truncate_blocks(f, newsize); - f->f_size = newsize; - flush_block(f); - return 0; -} - -// Flush the contents and metadata of file f out to disk. -// Loop over all the blocks in file. -// Translate the file block number into a disk block number -// and then check whether that disk block is dirty. If so, write it out. -void -file_flush(struct File *f) -{ - int i; - uint32_t *pdiskbno; - - for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) { - if (file_block_walk(f, i, &pdiskbno, 0) < 0 || - pdiskbno == NULL || *pdiskbno == 0) - continue; - flush_block(diskaddr(*pdiskbno)); - } - flush_block(f); - if (f->f_indirect) - flush_block(diskaddr(f->f_indirect)); -} - - -// Sync the entire file system. A big hammer. -void -fs_sync(void) -{ - int i; - for (i = 1; i < super->s_nblocks; i++) - flush_block(diskaddr(i)); -} - diff --git a/lab/Untitled Project.si4project/Backup/fs(7516).c b/lab/Untitled Project.si4project/Backup/fs(7516).c deleted file mode 100644 index 0b9b76e..0000000 --- a/lab/Untitled Project.si4project/Backup/fs(7516).c +++ /dev/null @@ -1,515 +0,0 @@ -#include -#include - -#include "fs.h" - -// -------------------------------------------------------------- -// Super block -// -------------------------------------------------------------- - -// Validate the file system super-block. -void -check_super(void) -{ - if (super->s_magic != FS_MAGIC) - panic("bad file system magic number"); - - if (super->s_nblocks > DISKSIZE/BLKSIZE) - panic("file system is too large"); - - cprintf("superblock is good\n"); -} - -// -------------------------------------------------------------- -// Free block bitmap -// -------------------------------------------------------------- - -// Check to see if the block bitmap indicates that block 'blockno' is free. -// Return 1 if the block is free, 0 if not. -bool -block_is_free(uint32_t blockno) -{ - if (super == 0 || blockno >= super->s_nblocks) - return 0; - if (bitmap[blockno / 32] & (1 << (blockno % 32))) - return 1; - return 0; -} - -// Mark a block free in the bitmap -void -free_block(uint32_t blockno) -{ - // Blockno zero is the null pointer of block numbers. - // 0 块启动块 - if (blockno == 0) - panic("attempt to free zero block"); - bitmap[blockno/32] |= 1<<(blockno%32); -} - -// Search the bitmap for a free block and allocate it. When you -// allocate a block, immediately flush the changed bitmap block -// to disk. -// -// Return block number allocated on success, -// -E_NO_DISK if we are out of blocks. -// -// Hint: use free_block as an example for manipulating the bitmap. -int -alloc_block(void) -{ - // The bitmap consists of one or more blocks. A single bitmap block - // contains the in-use bits for BLKBITSIZE blocks. There are - // super->s_nblocks blocks in the disk altogether. - - // LAB 5: Your code here. - size_t i; - for(i=1; i < super->s_nblocks; i++) { - if (block_is_free(i)) { - bitmap[i/32] &= ~(1<<(i%32)); - flush_block(&bitmap[i/32]); - return i; - } - } - // panic("alloc_block not implemented"); - return -E_NO_DISK; -} - -// Validate the file system bitmap. -// -// Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves -- -// are all marked as in-use. -void -check_bitmap(void) -{ - uint32_t i; - - // Make sure all bitmap blocks are marked in-use - for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++) - assert(!block_is_free(2+i)); - - // Make sure the reserved and root blocks are marked in-use. - assert(!block_is_free(0)); - assert(!block_is_free(1)); - - cprintf("bitmap is good\n"); -} - -// -------------------------------------------------------------- -// File system structures -// -------------------------------------------------------------- - - - -// Initialize the file system -void -fs_init(void) -{ - static_assert(sizeof(struct File) == 256); - - // Find a JOS disk. Use the second IDE disk (number 1) if available - if (ide_probe_disk1()) - ide_set_disk(1); - else - ide_set_disk(0); - bc_init(); - - // Set "super" to point to the super block. - super = diskaddr(1); - check_super(); - - // Set "bitmap" to the beginning of the first bitmap block. - bitmap = diskaddr(2); - check_bitmap(); - -} - -// Find the disk block number slot for the 'filebno'th block in file 'f'. -// Set '*ppdiskbno' to point to that slot. -// The slot will be one of the f->f_direct[] entries, -// or an entry in the indirect block. -// When 'alloc' is set, this function will allocate an indirect block -// if necessary. -// -// Returns: -// 0 on success (but note that *ppdiskbno might equal 0). -// -E_NOT_FOUND if the function needed to allocate an indirect block, but -// alloc was 0. -// -E_NO_DISK if there's no space on the disk for an indirect block. -// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT). -// -// Analogy: This is like pgdir_walk for files. -// Hint: Don't forget to clear any block you allocate. -static int -file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc) -{ - // LAB 5: Your code here. - // ppdiskbno 块指针 - if (filebno < NDIRECT) { - // but note that *ppdiskbno might equal 0 - if(ppdiskbno) - *ppdiskbno = &(f->f_direct[filebno]); - - return 0; - } - - if (filebno >= (NDIRECT + NINDIRECT)) - return -E_INVAL; - - filebno -= NDIRECT; - // indirect 还未分配 - if (!f->f_indirect) { - if (alloc == 0) - return -E_NOT_FOUND; - // 分配一个 indirect block - uint32_t blockno; - if ( (blockno = alloc_block()) < 0) - return blockno; - // f_indirect 直接记录块号,而不是记地址 - // f->f_indirect = (uint32_t)diskaddr(blockno); - f->f_indirect = blockno; - memset(diskaddr(blockno), 0, BLKSIZE); - flush_block(diskaddr(blockno)); - } - if (ppdiskbno) - *ppdiskbno = (uint32_t *)diskaddr(f->f_indirect) + filebno; - return 0; - // panic("file_block_walk not implemented"); -} - -// Set *blk to the address in memory where the filebno'th -// block of file 'f' would be mapped. -// -// Returns 0 on success, < 0 on error. Errors are: -// -E_NO_DISK if a block needed to be allocated but the disk is full. -// -E_INVAL if filebno is out of range. -// -// Hint: Use file_block_walk and alloc_block. -int -file_get_block(struct File *f, uint32_t filebno, char **blk) -{ - // LAB 5: Your code here. - uint32_t *pdiskbno; - int r; - if ( (r = file_block_walk(f, filebno, &pdiskbno, 1))< 0) - return r; - - if(*pdiskbno == 0) { - // 文件块还未分配 - if ( (r = alloc_block()) < 0) - return r; - *pdiskbno = r; - memset(diskaddr(r), 0, BLKSIZE); - flush_block(diskaddr(r)); - } - - // 最终指向块 - *blk = diskaddr(*pdiskbno); - return 0; - //panic("file_get_block not implemented"); -} - -// Try to find a file named "name" in dir. If so, set *file to it. -// -// Returns 0 and sets *file on success, < 0 on error. Errors are: -// -E_NOT_FOUND if the file is not found -static int -dir_lookup(struct File *dir, const char *name, struct File **file) -{ - int r; - uint32_t i, j, nblock; - char *blk; - struct File *f; - - // Search dir for name. - // We maintain the invariant that the size of a directory-file - // is always a multiple of the file system's block size. - // 目录size 必须为 文件系统块size的倍数。 - assert((dir->f_size % BLKSIZE) == 0); - nblock = dir->f_size / BLKSIZE; - for (i = 0; i < nblock; i++) { - if ((r = file_get_block(dir, i, &blk)) < 0) - return r; - f = (struct File*) blk; - for (j = 0; j < BLKFILES; j++) - // 不会出现子目录与文件同名吗? - if (strcmp(f[j].f_name, name) == 0) { - *file = &f[j]; - return 0; - } - } - return -E_NOT_FOUND; -} - -// Set *file to point at a free File structure in dir. The caller is -// responsible for filling in the File fields. -static int -dir_alloc_file(struct File *dir, struct File **file) -{ - int r; - uint32_t nblock, i, j; - char *blk; - struct File *f; - - assert((dir->f_size % BLKSIZE) == 0); - nblock = dir->f_size / BLKSIZE; - for (i = 0; i < nblock; i++) { - if ((r = file_get_block(dir, i, &blk)) < 0) - return r; - f = (struct File*) blk; - for (j = 0; j < BLKFILES; j++) - if (f[j].f_name[0] == '\0') { - *file = &f[j]; - return 0; - } - } - // 目录里没有空项,增添一个块 - dir->f_size += BLKSIZE; - if ((r = file_get_block(dir, i, &blk)) < 0) - return r; - f = (struct File*) blk; - *file = &f[0]; - return 0; -} - -// Skip over slashes. -static const char* -skip_slash(const char *p) -{ - while (*p == '/') - p++; - return p; -} - -// Evaluate a path name, starting at the root. -// On success, set *pf to the file we found -// and set *pdir to the directory the file is in. -// If we cannot find the file but find the directory -// it should be in, set *pdir and copy the final path -// element into lastelem. -static int -walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem) -{ - const char *p; - char name[MAXNAMELEN]; - struct File *dir, *f; - int r; - - // if (*path != '/') - // return -E_BAD_PATH; - path = skip_slash(path); - f = &super->s_root; - dir = 0; - name[0] = 0; - - if (pdir) - *pdir = 0; - *pf = 0; - while (*path != '\0') { - dir = f; - p = path; - while (*path != '/' && *path != '\0') - path++; - if (path - p >= MAXNAMELEN) - return -E_BAD_PATH; - memmove(name, p, path - p); - name[path - p] = '\0'; - path = skip_slash(path); - - if (dir->f_type != FTYPE_DIR) - return -E_NOT_FOUND; - - if ((r = dir_lookup(dir, name, &f)) < 0) { - if (r == -E_NOT_FOUND && *path == '\0') { - if (pdir) - *pdir = dir; - if (lastelem) - strcpy(lastelem, name); - *pf = 0; - } - return r; - } - } - - if (pdir) - *pdir = dir; - *pf = f; - return 0; -} - -// -------------------------------------------------------------- -// File operations -// -------------------------------------------------------------- - -// Create "path". On success set *pf to point at the file and return 0. -// On error return < 0. -int -file_create(const char *path, struct File **pf) -{ - char name[MAXNAMELEN]; - int r; - struct File *dir, *f; - - if ((r = walk_path(path, &dir, &f, name)) == 0) - return -E_FILE_EXISTS; - if (r != -E_NOT_FOUND || dir == 0) - return r; - if ((r = dir_alloc_file(dir, &f)) < 0) - return r; - - strcpy(f->f_name, name); - *pf = f; - file_flush(dir); - return 0; -} - -// Open "path". On success set *pf to point at the file and return 0. -// On error return < 0. -int -file_open(const char *path, struct File **pf) -{ - return walk_path(path, 0, pf, 0); -} - -// Read count bytes from f into buf, starting from seek position -// offset. This meant to mimic the standard pread function. -// Returns the number of bytes read, < 0 on error. -ssize_t -file_read(struct File *f, void *buf, size_t count, off_t offset) -{ - int r, bn; - off_t pos; - char *blk; - - if (offset >= f->f_size) - return 0; - - count = MIN(count, f->f_size - offset); - - for (pos = offset; pos < offset + count; ) { - if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0) - return r; - bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos); - memmove(buf, blk + pos % BLKSIZE, bn); - pos += bn; - buf += bn; - } - - return count; -} - - -// Write count bytes from buf into f, starting at seek position -// offset. This is meant to mimic the standard pwrite function. -// Extends the file if necessary. -// Returns the number of bytes written, < 0 on error. -int -file_write(struct File *f, const void *buf, size_t count, off_t offset) -{ - int r, bn; - off_t pos; - char *blk; - - // Extend file if necessary - if (offset + count > f->f_size) - if ((r = file_set_size(f, offset + count)) < 0) - return r; - - for (pos = offset; pos < offset + count; ) { - if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0) - return r; - bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos); - memmove(blk + pos % BLKSIZE, buf, bn); - pos += bn; - buf += bn; - } - - return count; -} - -// Remove a block from file f. If it's not there, just silently succeed. -// Returns 0 on success, < 0 on error. -static int -file_free_block(struct File *f, uint32_t filebno) -{ - int r; - uint32_t *ptr; - - if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0) - return r; - if (*ptr) { - free_block(*ptr); - *ptr = 0; - } - return 0; -} - -// Remove any blocks currently used by file 'f', -// but not necessary for a file of size 'newsize'. -// For both the old and new sizes, figure out the number of blocks required, -// and then clear the blocks from new_nblocks to old_nblocks. -// If the new_nblocks is no more than NDIRECT, and the indirect block has -// been allocated (f->f_indirect != 0), then free the indirect block too. -// (Remember to clear the f->f_indirect pointer so you'll know -// whether it's valid!) -// Do not change f->f_size. -static void -file_truncate_blocks(struct File *f, off_t newsize) -{ - int r; - uint32_t bno, old_nblocks, new_nblocks; - - old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE; - new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE; - for (bno = new_nblocks; bno < old_nblocks; bno++) - if ((r = file_free_block(f, bno)) < 0) - cprintf("warning: file_free_block: %e", r); - - if (new_nblocks <= NDIRECT && f->f_indirect) { - free_block(f->f_indirect); - f->f_indirect = 0; - } -} - -// Set the size of file f, truncating or extending as necessary. -int -file_set_size(struct File *f, off_t newsize) -{ - if (f->f_size > newsize) - file_truncate_blocks(f, newsize); - f->f_size = newsize; - flush_block(f); - return 0; -} - -// Flush the contents and metadata of file f out to disk. -// Loop over all the blocks in file. -// Translate the file block number into a disk block number -// and then check whether that disk block is dirty. If so, write it out. -void -file_flush(struct File *f) -{ - int i; - uint32_t *pdiskbno; - - for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) { - if (file_block_walk(f, i, &pdiskbno, 0) < 0 || - pdiskbno == NULL || *pdiskbno == 0) - continue; - flush_block(diskaddr(*pdiskbno)); - } - flush_block(f); - if (f->f_indirect) - flush_block(diskaddr(f->f_indirect)); -} - - -// Sync the entire file system. A big hammer. -void -fs_sync(void) -{ - int i; - for (i = 1; i < super->s_nblocks; i++) - flush_block(diskaddr(i)); -} - diff --git a/lab/Untitled Project.si4project/Backup/init(5052).c b/lab/Untitled Project.si4project/Backup/init(5052).c deleted file mode 100644 index 0ca8221..0000000 --- a/lab/Untitled Project.si4project/Backup/init(5052).c +++ /dev/null @@ -1,180 +0,0 @@ -/* See COPYRIGHT for copyright information. */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void boot_aps(void); - - -void -i386_init(void) -{ - // Initialize the console. - // Can't call cprintf until after we do this! - cons_init(); - - cprintf("6828 decimal is %o octal!\n", 6828); - - // Lab 2 memory management initialization functions - mem_init(); - - // Lab 3 user environment initialization functions - env_init(); - trap_init(); - - // Lab 4 multiprocessor initialization functions - mp_init(); - lapic_init(); - - // Lab 4 multitasking initialization functions - pic_init(); - - // Acquire the big kernel lock before waking up APs - // Your code here: - lock_kernel(); - // Starting non-boot CPUs - boot_aps(); - - // Start fs. - ENV_CREATE(fs_fs, ENV_TYPE_FS); - -#if defined(TEST) - // Don't touch -- used by grading script! - ENV_CREATE(TEST, ENV_TYPE_USER); -#else - // Touch all you want. -<<<<<<< HEAD - ENV_CREATE(user_icode, ENV_TYPE_USER); -======= - //ENV_CREATE(user_primes, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - - ->>>>>>> lab4 -#endif // TEST* - - // Should not be necessary - drains keyboard because interrupt has given up. - kbd_intr(); - - // Schedule and run the first user environment! - sched_yield(); -} - -// While boot_aps is booting a given CPU, it communicates the per-core -// stack pointer that should be loaded by mpentry.S to that CPU in -// this variable. -void *mpentry_kstack; - -// Start the non-boot (AP) processors. -static void -boot_aps(void) -{ - extern unsigned char mpentry_start[], mpentry_end[]; - void *code; - struct CpuInfo *c; - - // Write entry code to unused memory at MPENTRY_PADDR - code = KADDR(MPENTRY_PADDR); - memmove(code, mpentry_start, mpentry_end - mpentry_start); - - // Boot each AP one at a time - for (c = cpus; c < cpus + ncpu; c++) { - if (c == cpus + cpunum()) // We've started already. - continue; - - // Tell mpentry.S what stack to use - mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE; - // Start the CPU at mpentry_start - lapic_startap(c->cpu_id, PADDR(code)); - // Wait for the CPU to finish some basic setup in mp_main() - while(c->cpu_status != CPU_STARTED) - ; - } -} - -// Setup code for APs -void -mp_main(void) -{ - // We are in high EIP now, safe to switch to kern_pgdir - lcr3(PADDR(kern_pgdir)); - cprintf("SMP: CPU %d starting\n", cpunum()); - - lapic_init(); - env_init_percpu(); - trap_init_percpu(); - xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up - - // Now that we have finished some basic setup, call sched_yield() - // to start running processes on this CPU. But make sure that - // only one CPU can enter the scheduler at a time! - // - // Your code here: - lock_kernel(); - - sched_yield(); - // Remove this after you finish Exercise 6 - for (;;); -} - -/* - * Variable panicstr contains argument to first call to panic; used as flag - * to indicate that the kernel has already called panic. - */ -const char *panicstr; - -/* - * Panic is called on unresolvable fatal errors. - * It prints "panic: mesg", and then enters the kernel monitor. - */ -void -_panic(const char *file, int line, const char *fmt,...) -{ - va_list ap; - - if (panicstr) - goto dead; - panicstr = fmt; - - // Be extra sure that the machine is in as reasonable state - asm volatile("cli; cld"); - - va_start(ap, fmt); - cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line); - vcprintf(fmt, ap); - cprintf("\n"); - va_end(ap); - -dead: - /* break into the kernel monitor */ - while (1) - monitor(NULL); -} - -/* like panic, but don't */ -void -_warn(const char *file, int line, const char *fmt,...) -{ - va_list ap; - - va_start(ap, fmt); - cprintf("kernel warning at %s:%d: ", file, line); - vcprintf(fmt, ap); - cprintf("\n"); - va_end(ap); -} diff --git a/lab/Untitled Project.si4project/Backup/init(738).c b/lab/Untitled Project.si4project/Backup/init(738).c deleted file mode 100644 index 0086895..0000000 --- a/lab/Untitled Project.si4project/Backup/init(738).c +++ /dev/null @@ -1,172 +0,0 @@ -/* See COPYRIGHT for copyright information. */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void boot_aps(void); - - -void -i386_init(void) -{ - // Initialize the console. - // Can't call cprintf until after we do this! - cons_init(); - - cprintf("6828 decimal is %o octal!\n", 6828); - - // Lab 2 memory management initialization functions - mem_init(); - - // Lab 3 user environment initialization functions - env_init(); - trap_init(); - - // Lab 4 multiprocessor initialization functions - mp_init(); - lapic_init(); - - // Lab 4 multitasking initialization functions - pic_init(); - - // Acquire the big kernel lock before waking up APs - // Your code here: - lock_kernel(); - // Starting non-boot CPUs - boot_aps(); - - // Start fs. - ENV_CREATE(fs_fs, ENV_TYPE_FS); - -#if defined(TEST) - // Don't touch -- used by grading script! - ENV_CREATE(TEST, ENV_TYPE_USER); -#else - // Touch all you want. - ENV_CREATE(user_icode, ENV_TYPE_USER); - - -#endif // TEST* - - // Should not be necessary - drains keyboard because interrupt has given up. - kbd_intr(); - - // Schedule and run the first user environment! - sched_yield(); -} - -// While boot_aps is booting a given CPU, it communicates the per-core -// stack pointer that should be loaded by mpentry.S to that CPU in -// this variable. -void *mpentry_kstack; - -// Start the non-boot (AP) processors. -static void -boot_aps(void) -{ - extern unsigned char mpentry_start[], mpentry_end[]; - void *code; - struct CpuInfo *c; - - // Write entry code to unused memory at MPENTRY_PADDR - code = KADDR(MPENTRY_PADDR); - memmove(code, mpentry_start, mpentry_end - mpentry_start); - - // Boot each AP one at a time - for (c = cpus; c < cpus + ncpu; c++) { - if (c == cpus + cpunum()) // We've started already. - continue; - - // Tell mpentry.S what stack to use - mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE; - // Start the CPU at mpentry_start - lapic_startap(c->cpu_id, PADDR(code)); - // Wait for the CPU to finish some basic setup in mp_main() - while(c->cpu_status != CPU_STARTED) - ; - } -} - -// Setup code for APs -void -mp_main(void) -{ - // We are in high EIP now, safe to switch to kern_pgdir - lcr3(PADDR(kern_pgdir)); - cprintf("SMP: CPU %d starting\n", cpunum()); - - lapic_init(); - env_init_percpu(); - trap_init_percpu(); - xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up - - // Now that we have finished some basic setup, call sched_yield() - // to start running processes on this CPU. But make sure that - // only one CPU can enter the scheduler at a time! - // - // Your code here: - lock_kernel(); - - sched_yield(); - // Remove this after you finish Exercise 6 - for (;;); -} - -/* - * Variable panicstr contains argument to first call to panic; used as flag - * to indicate that the kernel has already called panic. - */ -const char *panicstr; - -/* - * Panic is called on unresolvable fatal errors. - * It prints "panic: mesg", and then enters the kernel monitor. - */ -void -_panic(const char *file, int line, const char *fmt,...) -{ - va_list ap; - - if (panicstr) - goto dead; - panicstr = fmt; - - // Be extra sure that the machine is in as reasonable state - asm volatile("cli; cld"); - - va_start(ap, fmt); - cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line); - vcprintf(fmt, ap); - cprintf("\n"); - va_end(ap); - -dead: - /* break into the kernel monitor */ - while (1) - monitor(NULL); -} - -/* like panic, but don't */ -void -_warn(const char *file, int line, const char *fmt,...) -{ - va_list ap; - - va_start(ap, fmt); - cprintf("kernel warning at %s:%d: ", file, line); - vcprintf(fmt, ap); - cprintf("\n"); - va_end(ap); -} diff --git a/lab/Untitled Project.si4project/Backup/init(7465).c b/lab/Untitled Project.si4project/Backup/init(7465).c deleted file mode 100644 index afe4745..0000000 --- a/lab/Untitled Project.si4project/Backup/init(7465).c +++ /dev/null @@ -1,174 +0,0 @@ -/* See COPYRIGHT for copyright information. */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void boot_aps(void); - - -void -i386_init(void) -{ - // Initialize the console. - // Can't call cprintf until after we do this! - cons_init(); - - cprintf("6828 decimal is %o octal!\n", 6828); - - // Lab 2 memory management initialization functions - mem_init(); - - // Lab 3 user environment initialization functions - env_init(); - trap_init(); - - // Lab 4 multiprocessor initialization functions - mp_init(); - lapic_init(); - - // Lab 4 multitasking initialization functions - pic_init(); - - // Acquire the big kernel lock before waking up APs - // Your code here: - lock_kernel(); - // Starting non-boot CPUs - boot_aps(); - - // Start fs. - ENV_CREATE(fs_fs, ENV_TYPE_FS); - -#if defined(TEST) - // Don't touch -- used by grading script! - ENV_CREATE(TEST, ENV_TYPE_USER); -#else - // Touch all you want. - ENV_CREATE(user_yield, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - ENV_CREATE(user_yield, ENV_TYPE_USER); - -#endif // TEST* - - // Should not be necessary - drains keyboard because interrupt has given up. - kbd_intr(); - - // Schedule and run the first user environment! - sched_yield(); -} - -// While boot_aps is booting a given CPU, it communicates the per-core -// stack pointer that should be loaded by mpentry.S to that CPU in -// this variable. -void *mpentry_kstack; - -// Start the non-boot (AP) processors. -static void -boot_aps(void) -{ - extern unsigned char mpentry_start[], mpentry_end[]; - void *code; - struct CpuInfo *c; - - // Write entry code to unused memory at MPENTRY_PADDR - code = KADDR(MPENTRY_PADDR); - memmove(code, mpentry_start, mpentry_end - mpentry_start); - - // Boot each AP one at a time - for (c = cpus; c < cpus + ncpu; c++) { - if (c == cpus + cpunum()) // We've started already. - continue; - - // Tell mpentry.S what stack to use - mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE; - // Start the CPU at mpentry_start - lapic_startap(c->cpu_id, PADDR(code)); - // Wait for the CPU to finish some basic setup in mp_main() - while(c->cpu_status != CPU_STARTED) - ; - } -} - -// Setup code for APs -void -mp_main(void) -{ - // We are in high EIP now, safe to switch to kern_pgdir - lcr3(PADDR(kern_pgdir)); - cprintf("SMP: CPU %d starting\n", cpunum()); - - lapic_init(); - env_init_percpu(); - trap_init_percpu(); - xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up - - // Now that we have finished some basic setup, call sched_yield() - // to start running processes on this CPU. But make sure that - // only one CPU can enter the scheduler at a time! - // - // Your code here: - lock_kernel(); - - sched_yield(); - // Remove this after you finish Exercise 6 - for (;;); -} - -/* - * Variable panicstr contains argument to first call to panic; used as flag - * to indicate that the kernel has already called panic. - */ -const char *panicstr; - -/* - * Panic is called on unresolvable fatal errors. - * It prints "panic: mesg", and then enters the kernel monitor. - */ -void -_panic(const char *file, int line, const char *fmt,...) -{ - va_list ap; - - if (panicstr) - goto dead; - panicstr = fmt; - - // Be extra sure that the machine is in as reasonable state - asm volatile("cli; cld"); - - va_start(ap, fmt); - cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line); - vcprintf(fmt, ap); - cprintf("\n"); - va_end(ap); - -dead: - /* break into the kernel monitor */ - while (1) - monitor(NULL); -} - -/* like panic, but don't */ -void -_warn(const char *file, int line, const char *fmt,...) -{ - va_list ap; - - va_start(ap, fmt); - cprintf("kernel warning at %s:%d: ", file, line); - vcprintf(fmt, ap); - cprintf("\n"); - va_end(ap); -} diff --git a/lab/Untitled Project.si4project/Backup/serv(2429).c b/lab/Untitled Project.si4project/Backup/serv(2429).c deleted file mode 100644 index 76c1d99..0000000 --- a/lab/Untitled Project.si4project/Backup/serv(2429).c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * File system server main loop - - * serves IPC requests from other environments. - */ - -#include -#include - -#include "fs.h" - - -#define debug 0 - -// The file system server maintains three structures -// for each open file. -// -// 1. The on-disk 'struct File' is mapped into the part of memory -// that maps the disk. This memory is kept private to the file -// server. -// 2. Each open file has a 'struct Fd' as well, which sort of -// corresponds to a Unix file descriptor. This 'struct Fd' is kept -// on *its own page* in memory, and it is shared with any -// environments that have the file open. -// 3. 'struct OpenFile' links these other two structures, and is kept -// private to the file server. The server maintains an array of -// all open files, indexed by "file ID". (There can be at most -// MAXOPEN files open concurrently.) The client uses file IDs to -// communicate with the server. File IDs are a lot like -// environment IDs in the kernel. Use openfile_lookup to translate -// file IDs to struct OpenFile. - -struct OpenFile { - uint32_t o_fileid; // file id - struct File *o_file; // mapped descriptor for open file - int o_mode; // open mode - struct Fd *o_fd; // Fd page -}; - -// Max number of open files in the file system at once -#define MAXOPEN 1024 -#define FILEVA 0xD0000000 - -// initialize to force into data section -struct OpenFile opentab[MAXOPEN] = { - { 0, 0, 1, 0 } -}; - -// Virtual address at which to receive page mappings containing client requests. -union Fsipc *fsreq = (union Fsipc *)0x0ffff000; - -void -serve_init(void) -{ - int i; - uintptr_t va = FILEVA; - for (i = 0; i < MAXOPEN; i++) { - opentab[i].o_fileid = i; - opentab[i].o_fd = (struct Fd*) va; - va += PGSIZE; - } -} - -// Allocate an open file. -int -openfile_alloc(struct OpenFile **o) -{ - int i, r; - - // Find an available open-file table entry - for (i = 0; i < MAXOPEN; i++) { - switch (pageref(opentab[i].o_fd)) { - case 0: - if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0) - return r; - /* fall through */ - case 1: - opentab[i].o_fileid += MAXOPEN; - *o = &opentab[i]; - memset(opentab[i].o_fd, 0, PGSIZE); - return (*o)->o_fileid; - } - } - return -E_MAX_OPEN; -} - -// Look up an open file for envid. -int -openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po) -{ - struct OpenFile *o; - - o = &opentab[fileid % MAXOPEN]; - if (pageref(o->o_fd) <= 1 || o->o_fileid != fileid) - return -E_INVAL; - *po = o; - return 0; -} - -// Open req->req_path in mode req->req_omode, storing the Fd page and -// permissions to return to the calling environment in *pg_store and -// *perm_store respectively. -int -serve_open(envid_t envid, struct Fsreq_open *req, - void **pg_store, int *perm_store) -{ - char path[MAXPATHLEN]; - struct File *f; - int fileid; - int r; - struct OpenFile *o; - - if (debug) - cprintf("serve_open %08x %s 0x%x\n", envid, req->req_path, req->req_omode); - - // Copy in the path, making sure it's null-terminated - memmove(path, req->req_path, MAXPATHLEN); - path[MAXPATHLEN-1] = 0; - - // Find an open file ID - if ((r = openfile_alloc(&o)) < 0) { - if (debug) - cprintf("openfile_alloc failed: %e", r); - return r; - } - fileid = r; - - // Open the file - if (req->req_omode & O_CREAT) { - if ((r = file_create(path, &f)) < 0) { - if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS) - goto try_open; - if (debug) - cprintf("file_create failed: %e", r); - return r; - } - } else { -try_open: - if ((r = file_open(path, &f)) < 0) { - if (debug) - cprintf("file_open failed: %e", r); - return r; - } - } - - // Truncate - if (req->req_omode & O_TRUNC) { - if ((r = file_set_size(f, 0)) < 0) { - if (debug) - cprintf("file_set_size failed: %e", r); - return r; - } - } - if ((r = file_open(path, &f)) < 0) { - if (debug) - cprintf("file_open failed: %e", r); - return r; - } - - // Save the file pointer - o->o_file = f; - - // Fill out the Fd structure - o->o_fd->fd_file.id = o->o_fileid; - o->o_fd->fd_omode = req->req_omode & O_ACCMODE; - o->o_fd->fd_dev_id = devfile.dev_id; - o->o_mode = req->req_omode; - - if (debug) - cprintf("sending success, page %08x\n", (uintptr_t) o->o_fd); - - // Share the FD page with the caller by setting *pg_store, - // store its permission in *perm_store - *pg_store = o->o_fd; - *perm_store = PTE_P|PTE_U|PTE_W|PTE_SHARE; - - return 0; -} - -// Set the size of req->req_fileid to req->req_size bytes, truncating -// or extending the file as necessary. -int -serve_set_size(envid_t envid, struct Fsreq_set_size *req) -{ - struct OpenFile *o; - int r; - - if (debug) - cprintf("serve_set_size %08x %08x %08x\n", envid, req->req_fileid, req->req_size); - - // Every file system IPC call has the same general structure. - // Here's how it goes. - - // First, use openfile_lookup to find the relevant open file. - // On failure, return the error code to the client with ipc_send. - if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0) - return r; - - // Second, call the relevant file system function (from fs/fs.c). - // On failure, return the error code to the client. - return file_set_size(o->o_file, req->req_size); -} - -// Read at most ipc->read.req_n bytes from the current seek position -// in ipc->read.req_fileid. Return the bytes read from the file to -// the caller in ipc->readRet, then update the seek position. Returns -// the number of bytes successfully read, or < 0 on error. -int -serve_read(envid_t envid, union Fsipc *ipc) -{ - struct Fsreq_read *req = &ipc->read; - struct Fsret_read *ret = &ipc->readRet; - - if (debug) - cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n); - - // Lab 5: Your code here: - return 0; -} - - -// Write req->req_n bytes from req->req_buf to req_fileid, starting at -// the current seek position, and update the seek position -// accordingly. Extend the file if necessary. Returns the number of -// bytes written, or < 0 on error. -int -serve_write(envid_t envid, struct Fsreq_write *req) -{ - if (debug) - cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n); - - // LAB 5: Your code here. - panic("serve_write not implemented"); -} - -// Stat ipc->stat.req_fileid. Return the file's struct Stat to the -// caller in ipc->statRet. -int -serve_stat(envid_t envid, union Fsipc *ipc) -{ - struct Fsreq_stat *req = &ipc->stat; - struct Fsret_stat *ret = &ipc->statRet; - struct OpenFile *o; - int r; - - if (debug) - cprintf("serve_stat %08x %08x\n", envid, req->req_fileid); - - if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0) - return r; - - strcpy(ret->ret_name, o->o_file->f_name); - ret->ret_size = o->o_file->f_size; - ret->ret_isdir = (o->o_file->f_type == FTYPE_DIR); - return 0; -} - -// Flush all data and metadata of req->req_fileid to disk. -int -serve_flush(envid_t envid, struct Fsreq_flush *req) -{ - struct OpenFile *o; - int r; - - if (debug) - cprintf("serve_flush %08x %08x\n", envid, req->req_fileid); - - if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0) - return r; - file_flush(o->o_file); - return 0; -} - - -int -serve_sync(envid_t envid, union Fsipc *req) -{ - fs_sync(); - return 0; -} - -typedef int (*fshandler)(envid_t envid, union Fsipc *req); - -fshandler handlers[] = { - // Open is handled specially because it passes pages - /* [FSREQ_OPEN] = (fshandler)serve_open, */ - [FSREQ_READ] = serve_read, - [FSREQ_STAT] = serve_stat, - [FSREQ_FLUSH] = (fshandler)serve_flush, - [FSREQ_WRITE] = (fshandler)serve_write, - [FSREQ_SET_SIZE] = (fshandler)serve_set_size, - [FSREQ_SYNC] = serve_sync -}; - -void -serve(void) -{ - uint32_t req, whom; - int perm, r; - void *pg; - - while (1) { - perm = 0; - req = ipc_recv((int32_t *) &whom, fsreq, &perm); - if (debug) - cprintf("fs req %d from %08x [page %08x: %s]\n", - req, whom, uvpt[PGNUM(fsreq)], fsreq); - - // All requests must contain an argument page - if (!(perm & PTE_P)) { - cprintf("Invalid request from %08x: no argument page\n", - whom); - continue; // just leave it hanging... - } - - pg = NULL; - if (req == FSREQ_OPEN) { - r = serve_open(whom, (struct Fsreq_open*)fsreq, &pg, &perm); - } else if (req < ARRAY_SIZE(handlers) && handlers[req]) { - r = handlers[req](whom, fsreq); - } else { - cprintf("Invalid request code %d from %08x\n", req, whom); - r = -E_INVAL; - } - ipc_send(whom, r, pg, perm); - sys_page_unmap(0, fsreq); - } -} - -void -umain(int argc, char **argv) -{ - static_assert(sizeof(struct File) == 256); - binaryname = "fs"; - cprintf("FS is running\n"); - - // Check that we are able to do I/O - outw(0x8A00, 0x8A00); - cprintf("FS can do I/O\n"); - - serve_init(); - fs_init(); - fs_test(); - serve(); -} - diff --git a/lab/Untitled Project.si4project/Backup/sh(7186).c b/lab/Untitled Project.si4project/Backup/sh(7186).c deleted file mode 100644 index 26f501a..0000000 --- a/lab/Untitled Project.si4project/Backup/sh(7186).c +++ /dev/null @@ -1,322 +0,0 @@ -#include - -#define BUFSIZ 1024 /* Find the buffer overrun bug! */ -int debug = 0; - - -// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0. -// gettoken(0, token) parses a shell token from the previously set string, -// null-terminates that token, stores the token pointer in '*token', -// and returns a token ID (0, '<', '>', '|', or 'w'). -// Subsequent calls to 'gettoken(0, token)' will return subsequent -// tokens from the string. -int gettoken(char *s, char **token); - - -// Parse a shell command from string 's' and execute it. -// Do not return until the shell command is finished. -// runcmd() is called in a forked child, -// so it's OK to manipulate file descriptor state. -#define MAXARGS 16 -void -runcmd(char* s) -{ - char *argv[MAXARGS], *t, argv0buf[BUFSIZ]; - int argc, c, i, r, p[2], fd, pipe_child; - - pipe_child = 0; - gettoken(s, 0); - -again: - argc = 0; - while (1) { - switch ((c = gettoken(0, &t))) { - - case 'w': // Add an argument - if (argc == MAXARGS) { - cprintf("too many arguments\n"); - exit(); - } - argv[argc++] = t; - break; - - case '<': // Input redirection - // Grab the filename from the argument list - if (gettoken(0, &t) != 'w') { - cprintf("syntax error: < not followed by word\n"); - exit(); - } - // Open 't' for reading as file descriptor 0 - // (which environments use as standard input). - // We can't open a file onto a particular descriptor, - // so open the file as 'fd', - // then check whether 'fd' is 0. - // If not, dup 'fd' onto file descriptor 0, - // then close the original 'fd'. - - // LAB 5: Your code here. - panic("< redirection not implemented"); - break; - - case '>': // Output redirection - // Grab the filename from the argument list - if (gettoken(0, &t) != 'w') { - cprintf("syntax error: > not followed by word\n"); - exit(); - } - if ((fd = open(t, O_WRONLY|O_CREAT|O_TRUNC)) < 0) { - cprintf("open %s for write: %e", t, fd); - exit(); - } - if (fd != 1) { - dup(fd, 1); - close(fd); - } - break; - - case '|': // Pipe - if ((r = pipe(p)) < 0) { - cprintf("pipe: %e", r); - exit(); - } - if (debug) - cprintf("PIPE: %d %d\n", p[0], p[1]); - if ((r = fork()) < 0) { - cprintf("fork: %e", r); - exit(); - } - if (r == 0) { - if (p[0] != 0) { - dup(p[0], 0); - close(p[0]); - } - close(p[1]); - goto again; - } else { - pipe_child = r; - if (p[1] != 1) { - dup(p[1], 1); - close(p[1]); - } - close(p[0]); - goto runit; - } - panic("| not implemented"); - break; - - case 0: // String is complete - // Run the current command! - goto runit; - - default: - panic("bad return %d from gettoken", c); - break; - - } - } - -runit: - // Return immediately if command line was empty. - if(argc == 0) { - if (debug) - cprintf("EMPTY COMMAND\n"); - return; - } - - // Clean up command line. - // Read all commands from the filesystem: add an initial '/' to - // the command name. - // This essentially acts like 'PATH=/'. - if (argv[0][0] != '/') { - argv0buf[0] = '/'; - strcpy(argv0buf + 1, argv[0]); - argv[0] = argv0buf; - } - argv[argc] = 0; - - // Print the command. - if (debug) { - cprintf("[%08x] SPAWN:", thisenv->env_id); - for (i = 0; argv[i]; i++) - cprintf(" %s", argv[i]); - cprintf("\n"); - } - - // Spawn the command! - if ((r = spawn(argv[0], (const char**) argv)) < 0) - cprintf("spawn %s: %e\n", argv[0], r); - - // In the parent, close all file descriptors and wait for the - // spawned command to exit. - close_all(); - if (r >= 0) { - if (debug) - cprintf("[%08x] WAIT %s %08x\n", thisenv->env_id, argv[0], r); - wait(r); - if (debug) - cprintf("[%08x] wait finished\n", thisenv->env_id); - } - - // If we were the left-hand part of a pipe, - // wait for the right-hand part to finish. - if (pipe_child) { - if (debug) - cprintf("[%08x] WAIT pipe_child %08x\n", thisenv->env_id, pipe_child); - wait(pipe_child); - if (debug) - cprintf("[%08x] wait finished\n", thisenv->env_id); - } - - // Done! - exit(); -} - - -// Get the next token from string s. -// Set *p1 to the beginning of the token and *p2 just past the token. -// Returns -// 0 for end-of-string; -// < for <; -// > for >; -// | for |; -// w for a word. -// -// Eventually (once we parse the space where the \0 will go), -// words get nul-terminated. -#define WHITESPACE " \t\r\n" -#define SYMBOLS "<|>&;()" - -int -_gettoken(char *s, char **p1, char **p2) -{ - int t; - - if (s == 0) { - if (debug > 1) - cprintf("GETTOKEN NULL\n"); - return 0; - } - - if (debug > 1) - cprintf("GETTOKEN: %s\n", s); - - *p1 = 0; - *p2 = 0; - - while (strchr(WHITESPACE, *s)) - *s++ = 0; - if (*s == 0) { - if (debug > 1) - cprintf("EOL\n"); - return 0; - } - if (strchr(SYMBOLS, *s)) { - t = *s; - *p1 = s; - *s++ = 0; - *p2 = s; - if (debug > 1) - cprintf("TOK %c\n", t); - return t; - } - *p1 = s; - while (*s && !strchr(WHITESPACE SYMBOLS, *s)) - s++; - *p2 = s; - if (debug > 1) { - t = **p2; - **p2 = 0; - cprintf("WORD: %s\n", *p1); - **p2 = t; - } - return 'w'; -} - -int -gettoken(char *s, char **p1) -{ - static int c, nc; - static char* np1, *np2; - - if (s) { - nc = _gettoken(s, &np1, &np2); - return 0; - } - c = nc; - *p1 = np1; - nc = _gettoken(np2, &np1, &np2); - return c; -} - - -void -usage(void) -{ - cprintf("usage: sh [-dix] [command-file]\n"); - exit(); -} - -void -umain(int argc, char **argv) -{ - int r, interactive, echocmds; - struct Argstate args; - - interactive = '?'; - echocmds = 0; - argstart(&argc, argv, &args); - while ((r = argnext(&args)) >= 0) - switch (r) { - case 'd': - debug++; - break; - case 'i': - interactive = 1; - break; - case 'x': - echocmds = 1; - break; - default: - usage(); - } - - if (argc > 2) - usage(); - if (argc == 2) { - close(0); - if ((r = open(argv[1], O_RDONLY)) < 0) - panic("open %s: %e", argv[1], r); - assert(r == 0); - } - if (interactive == '?') - interactive = iscons(0); - - while (1) { - char *buf; - - buf = readline(interactive ? "$ " : NULL); - if (buf == NULL) { - if (debug) - cprintf("EXITING\n"); - exit(); // end of file - } - if (debug) - cprintf("LINE: %s\n", buf); - if (buf[0] == '#') - continue; - if (echocmds) - printf("# %s\n", buf); - if (debug) - cprintf("BEFORE FORK\n"); - if ((r = fork()) < 0) - panic("fork: %e", r); - if (debug) - cprintf("FORK: %d\n", r); - if (r == 0) { - runcmd(buf); - exit(); - } else - wait(r); - } -} - diff --git a/lab/Untitled Project.si4project/Backup/spawn(5260).c b/lab/Untitled Project.si4project/Backup/spawn(5260).c deleted file mode 100644 index 9d0eb07..0000000 --- a/lab/Untitled Project.si4project/Backup/spawn(5260).c +++ /dev/null @@ -1,307 +0,0 @@ -#include -#include - -#define UTEMP2USTACK(addr) ((void*) (addr) + (USTACKTOP - PGSIZE) - UTEMP) -#define UTEMP2 (UTEMP + PGSIZE) -#define UTEMP3 (UTEMP2 + PGSIZE) - -// Helper functions for spawn. -static int init_stack(envid_t child, const char **argv, uintptr_t *init_esp); -static int map_segment(envid_t child, uintptr_t va, size_t memsz, - int fd, size_t filesz, off_t fileoffset, int perm); -static int copy_shared_pages(envid_t child); - -// Spawn a child process from a program image loaded from the file system. -// prog: the pathname of the program to run. -// argv: pointer to null-terminated array of pointers to strings, -// which will be passed to the child as its command-line arguments. -// Returns child envid on success, < 0 on failure. -int -spawn(const char *prog, const char **argv) -{ - unsigned char elf_buf[512]; - struct Trapframe child_tf; - envid_t child; - - int fd, i, r; - struct Elf *elf; - struct Proghdr *ph; - int perm; - - // This code follows this procedure: - // - // - Open the program file. - // - // - Read the ELF header, as you have before, and sanity check its - // magic number. (Check out your load_icode!) - // - // - Use sys_exofork() to create a new environment. - // - // - Set child_tf to an initial struct Trapframe for the child. - // - // - Call the init_stack() function above to set up - // the initial stack page for the child environment. - // - // - Map all of the program's segments that are of p_type - // ELF_PROG_LOAD into the new environment's address space. - // Use the p_flags field in the Proghdr for each segment - // to determine how to map the segment: - // - // * If the ELF flags do not include ELF_PROG_FLAG_WRITE, - // then the segment contains text and read-only data. - // Use read_map() to read the contents of this segment, - // and map the pages it returns directly into the child - // so that multiple instances of the same program - // will share the same copy of the program text. - // Be sure to map the program text read-only in the child. - // Read_map is like read but returns a pointer to the data in - // *blk rather than copying the data into another buffer. - // - // * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE, - // then the segment contains read/write data and bss. - // As with load_icode() in Lab 3, such an ELF segment - // occupies p_memsz bytes in memory, but only the FIRST - // p_filesz bytes of the segment are actually loaded - // from the executable file - you must clear the rest to zero. - // For each page to be mapped for a read/write segment, - // allocate a page in the parent temporarily at UTEMP, - // read() the appropriate portion of the file into that page - // and/or use memset() to zero non-loaded portions. - // (You can avoid calling memset(), if you like, if - // page_alloc() returns zeroed pages already.) - // Then insert the page mapping into the child. - // Look at init_stack() for inspiration. - // Be sure you understand why you can't use read_map() here. - // - // Note: None of the segment addresses or lengths above - // are guaranteed to be page-aligned, so you must deal with - // these non-page-aligned values appropriately. - // The ELF linker does, however, guarantee that no two segments - // will overlap on the same page; and it guarantees that - // PGOFF(ph->p_offset) == PGOFF(ph->p_va). - // - // - Call sys_env_set_trapframe(child, &child_tf) to set up the - // correct initial eip and esp values in the child. - // - // - Start the child process running with sys_env_set_status(). - - if ((r = open(prog, O_RDONLY)) < 0) - return r; - fd = r; - - // Read elf header - elf = (struct Elf*) elf_buf; - if (readn(fd, elf_buf, sizeof(elf_buf)) != sizeof(elf_buf) - || elf->e_magic != ELF_MAGIC) { - close(fd); - cprintf("elf magic %08x want %08x\n", elf->e_magic, ELF_MAGIC); - return -E_NOT_EXEC; - } - - // Create new child environment - if ((r = sys_exofork()) < 0) - return r; - child = r; - - // Set up trap frame, including initial stack. - child_tf = envs[ENVX(child)].env_tf; - child_tf.tf_eip = elf->e_entry; - - if ((r = init_stack(child, argv, &child_tf.tf_esp)) < 0) - return r; - - // Set up program segments as defined in ELF header. - ph = (struct Proghdr*) (elf_buf + elf->e_phoff); - for (i = 0; i < elf->e_phnum; i++, ph++) { - if (ph->p_type != ELF_PROG_LOAD) - continue; - perm = PTE_P | PTE_U; - if (ph->p_flags & ELF_PROG_FLAG_WRITE) - perm |= PTE_W; - if ((r = map_segment(child, ph->p_va, ph->p_memsz, - fd, ph->p_filesz, ph->p_offset, perm)) < 0) - goto error; - } - close(fd); - fd = -1; - - // Copy shared library state. - if ((r = copy_shared_pages(child)) < 0) - panic("copy_shared_pages: %e", r); - - child_tf.tf_eflags |= FL_IOPL_3; // devious: see user/faultio.c - if ((r = sys_env_set_trapframe(child, &child_tf)) < 0) - panic("sys_env_set_trapframe: %e", r); - - if ((r = sys_env_set_status(child, ENV_RUNNABLE)) < 0) - panic("sys_env_set_status: %e", r); - - return child; - -error: - sys_env_destroy(child); - close(fd); - return r; -} - -// Spawn, taking command-line arguments array directly on the stack. -// NOTE: Must have a sentinal of NULL at the end of the args -// (none of the args may be NULL). -int -spawnl(const char *prog, const char *arg0, ...) -{ - // We calculate argc by advancing the args until we hit NULL. - // The contract of the function guarantees that the last - // argument will always be NULL, and that none of the other - // arguments will be NULL. - int argc=0; - va_list vl; - va_start(vl, arg0); - while(va_arg(vl, void *) != NULL) - argc++; - va_end(vl); - - // Now that we have the size of the args, do a second pass - // and store the values in a VLA, which has the format of argv - const char *argv[argc+2]; - argv[0] = arg0; - argv[argc+1] = NULL; - - va_start(vl, arg0); - unsigned i; - for(i=0;i= filesz) { - // allocate a blank page - if ((r = sys_page_alloc(child, (void*) (va + i), perm)) < 0) - return r; - } else { - // from file - if ((r = sys_page_alloc(0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0) - return r; - if ((r = seek(fd, fileoffset + i)) < 0) - return r; - if ((r = readn(fd, UTEMP, MIN(PGSIZE, filesz-i))) < 0) - return r; - if ((r = sys_page_map(0, UTEMP, child, (void*) (va + i), perm)) < 0) - panic("spawn: sys_page_map data: %e", r); - sys_page_unmap(0, UTEMP); - } - } - return 0; -} - -// Copy the mappings for shared pages into the child address space. -static int -copy_shared_pages(envid_t child) -{ - // LAB 5: Your code here. - return 0; -} - diff --git a/lab/Untitled Project.si4project/Backup/trap(3657).c b/lab/Untitled Project.si4project/Backup/trap(3657).c deleted file mode 100644 index c36b562..0000000 --- a/lab/Untitled Project.si4project/Backup/trap(3657).c +++ /dev/null @@ -1,436 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct Taskstate ts; - -/* For debugging, so print_trapframe can distinguish between printing - * a saved trapframe and printing the current trapframe and print some - * additional information in the latter case. - */ -static struct Trapframe *last_tf; - -/* Interrupt descriptor table. (Must be built at run time because - * shifted function addresses can't be represented in relocation records.) - */ -struct Gatedesc idt[256] = { { 0 } }; -struct Pseudodesc idt_pd = { - sizeof(idt) - 1, (uint32_t) idt -}; - - -static const char *trapname(int trapno) -{ - static const char * const excnames[] = { - "Divide error", - "Debug", - "Non-Maskable Interrupt", - "Breakpoint", - "Overflow", - "BOUND Range Exceeded", - "Invalid Opcode", - "Device Not Available", - "Double Fault", - "Coprocessor Segment Overrun", - "Invalid TSS", - "Segment Not Present", - "Stack Fault", - "General Protection", - "Page Fault", - "(unknown trap)", - "x87 FPU Floating-Point Error", - "Alignment Check", - "Machine-Check", - "SIMD Floating-Point Exception" - }; - - if (trapno < ARRAY_SIZE(excnames)) - return excnames[trapno]; - if (trapno == T_SYSCALL) - return "System call"; - if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) - return "Hardware Interrupt"; - return "(unknown trap)"; -} - -// You will also need to modify trap_init() to initialize the idt to -// point to each of these entry points defined in trapentry.S; -// the SETGATE macro will be helpful here -void -trap_init(void) -{ - - extern struct Segdesc gdt[]; - void divide_handler(); - void debug_handler(); - void nmi_handler(); - void brkpt_handler(); - void oflow_handler(); - void bound_handler(); - void device_handler(); - void illop_handler(); - void tss_handler(); - void segnp_handler(); - void stack_handler(); - void gpflt_handler(); - void pgflt_handler(); - void fperr_handler(); - void align_handler(); - void mchk_handler(); - void simderr_handler(); - void syscall_handler(); - void dblflt_handler(); - void timer_handler(); - void kbd_handler(); - void serial_handler(); - void spurious_handler(); - void ide_handler(); - void error_handler(); - - - // LAB 3: Your code here. - // GD_KT 全局描述符, kernel text - SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); - SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); - SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); - SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); - SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); - SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); - SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); - SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); - SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); - SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); - SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); - SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); - SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); - SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); - SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); - SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); - SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); - SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); - SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); - // IRQ - SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_handler, 3); - - // Per-CPU setup - trap_init_percpu(); -} - -// Initialize and load the per-CPU TSS and IDT -void -trap_init_percpu(void) -{ - // The example code here sets up the Task State Segment (TSS) and - // the TSS descriptor for CPU 0. But it is incorrect if we are - // running on other CPUs because each CPU has its own kernel stack. - // Fix the code so that it works for all CPUs. - // - // Hints: - // - The macro "thiscpu" always refers to the current CPU's - // struct CpuInfo; - // - The ID of the current CPU is given by cpunum() or - // thiscpu->cpu_id; - // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, - // rather than the global "ts" variable; - // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; - // - You mapped the per-CPU kernel stacks in mem_init_mp() - // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments - // from doing IO (0 is not the correct value!) - // - // ltr sets a 'busy' flag in the TSS selector, so if you - // accidentally load the same TSS on more than one CPU, you'll - // get a triple fault. If you set up an individual CPU's TSS - // wrong, you may not get a fault until you try to return from - // user space on that CPU. - // - // LAB 4: Your code here: - thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); - thiscpu->cpu_ts.ts_ss0 = GD_KD; - - // Initialize the TSS slot of the gdt. - gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); - gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; - - // Load the TSS selector (like other segment selectors, the - // bottom three bits are special; we leave them 0) - ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); - - // Load the IDT - lidt(&idt_pd); -} - -void -print_trapframe(struct Trapframe *tf) -{ - cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); - print_regs(&tf->tf_regs); - cprintf(" es 0x----%04x\n", tf->tf_es); - cprintf(" ds 0x----%04x\n", tf->tf_ds); - cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); - // If this trap was a page fault that just happened - // (so %cr2 is meaningful), print the faulting linear address. - if (tf == last_tf && tf->tf_trapno == T_PGFLT) - cprintf(" cr2 0x%08x\n", rcr2()); - cprintf(" err 0x%08x", tf->tf_err); - // For page faults, print decoded fault error code: - // U/K=fault occurred in user/kernel mode - // W/R=a write/read caused the fault - // PR=a protection violation caused the fault (NP=page not present). - if (tf->tf_trapno == T_PGFLT) - cprintf(" [%s, %s, %s]\n", - tf->tf_err & 4 ? "user" : "kernel", - tf->tf_err & 2 ? "write" : "read", - tf->tf_err & 1 ? "protection" : "not-present"); - else - cprintf("\n"); - cprintf(" eip 0x%08x\n", tf->tf_eip); - cprintf(" cs 0x----%04x\n", tf->tf_cs); - cprintf(" flag 0x%08x\n", tf->tf_eflags); - if ((tf->tf_cs & 3) != 0) { - cprintf(" esp 0x%08x\n", tf->tf_esp); - cprintf(" ss 0x----%04x\n", tf->tf_ss); - } -} - -void -print_regs(struct PushRegs *regs) -{ - cprintf(" edi 0x%08x\n", regs->reg_edi); - cprintf(" esi 0x%08x\n", regs->reg_esi); - cprintf(" ebp 0x%08x\n", regs->reg_ebp); - cprintf(" oesp 0x%08x\n", regs->reg_oesp); - cprintf(" ebx 0x%08x\n", regs->reg_ebx); - cprintf(" edx 0x%08x\n", regs->reg_edx); - cprintf(" ecx 0x%08x\n", regs->reg_ecx); - cprintf(" eax 0x%08x\n", regs->reg_eax); -} - -static void -trap_dispatch(struct Trapframe *tf) -{ - // Handle processor exceptions. - // LAB 3: Your code here. - switch(tf->tf_trapno) { - case T_PGFLT: - page_fault_handler(tf); - break; - case T_BRKPT: - monitor(tf); - break; - case T_SYSCALL: - - tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, - tf->tf_regs.reg_edx, - tf->tf_regs.reg_ecx, - tf->tf_regs.reg_ebx, - tf->tf_regs.reg_edi, - tf->tf_regs.reg_esi); - break; - case (IRQ_OFFSET + IRQ_TIMER): - // 回应8259A 接收中断。 - lapic_eoi(); - sched_yield(); - break; - - default: - // Unexpected trap: The user process or the kernel has a bug. - print_trapframe(tf); - if (tf->tf_cs == GD_KT) - panic("unhandled trap in kernel"); - else { - env_destroy(curenv); - return; - } - break; - - } - - // Handle clock interrupts. Don't forget to acknowledge the - // interrupt using lapic_eoi() before calling the scheduler! - // LAB 4: Your code here. - -<<<<<<< HEAD - // Handle keyboard and serial interrupts. - // LAB 5: Your code here. - - // Unexpected trap: The user process or the kernel has a bug. - print_trapframe(tf); - if (tf->tf_cs == GD_KT) - panic("unhandled trap in kernel"); - else { - env_destroy(curenv); - return; - } -======= - ->>>>>>> lab4 -} - -void -trap(struct Trapframe *tf) -{ - // The environment may have set DF and some versions - // of GCC rely on DF being clear - asm volatile("cld" ::: "cc"); - - // Halt the CPU if some other CPU has called panic() - extern char *panicstr; - if (panicstr) - asm volatile("hlt"); - - // Re-acqurie the big kernel lock if we were halted in - // sched_yield() - if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) - lock_kernel(); - // Check that interrupts are disabled. If this assertion - // fails, DO NOT be tempted to fix it by inserting a "cli" in - // the interrupt path. - assert(!(read_eflags() & FL_IF)); - - if ((tf->tf_cs & 3) == 3) { - // Trapped from user mode. - // Acquire the big kernel lock before doing any - // serious kernel work. - // LAB 4: Your code here. - lock_kernel(); - assert(curenv); - - // Garbage collect if current enviroment is a zombie - if (curenv->env_status == ENV_DYING) { - env_free(curenv); - curenv = NULL; - sched_yield(); - } - - // Copy trap frame (which is currently on the stack) - // into 'curenv->env_tf', so that running the environment - // will restart at the trap point. - curenv->env_tf = *tf; - // The trapframe on the stack should be ignored from here on. - tf = &curenv->env_tf; - } - - // Record that tf is the last real trapframe so - // print_trapframe can print some additional information. - last_tf = tf; - - // Dispatch based on what type of trap occurred - trap_dispatch(tf); - - // If we made it to this point, then no other environment was - // scheduled, so we should return to the current environment - // if doing so makes sense. - if (curenv && curenv->env_status == ENV_RUNNING) - env_run(curenv); - else - sched_yield(); -} - - -void -page_fault_handler(struct Trapframe *tf) -{ - uint32_t fault_va; - - // Read processor's CR2 register to find the faulting address - fault_va = rcr2(); - - // Handle kernel-mode page faults. - - // LAB 3: Your code here. - - // 怎么判断是内核模式, CPL位 - - if(tf->tf_cs && 3 == 0) { - panic("page_fault in kernel mode, fault address %d\n", fault_va); - } - - // We've already handled kernel-mode exceptions, so if we get here, - // the page fault happened in user mode. - - - // Call the environment's page fault upcall, if one exists. Set up a - // page fault stack frame on the user exception stack (below - // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. - // - // The page fault upcall might cause another page fault, in which case - // we branch to the page fault upcall recursively, pushing another - // page fault stack frame on top of the user exception stack. - // - // It is convenient for our code which returns from a page fault - // (lib/pfentry.S) to have one word of scratch space at the top of the - // trap-time stack; it allows us to more easily restore the eip/esp. In - // the non-recursive case, we don't have to worry about this because - // the top of the regular user stack is free. In the recursive case, - // this means we have to leave an extra word between the current top of - // the exception stack and the new stack frame because the exception - // stack _is_ the trap-time stack. - // - // If there's no page fault upcall, the environment didn't allocate a - // page for its exception stack or can't write to it, or the exception - // stack overflows, then destroy the environment that caused the fault. - // Note that the grade script assumes you will first check for the page - // fault upcall and print the "user fault va" message below if there is - // none. The remaining three checks can be combined into a single test. - // - // Hints: - // user_mem_assert() and env_run() are useful here. - // To change what the user environment runs, modify 'curenv->env_tf' - // (the 'tf' variable points at 'curenv->env_tf'). - - // LAB 4: Your code here. - struct UTrapframe *utf; - // cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va); - if (curenv->env_pgfault_upcall) { - - if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { - // 异常模式下陷入 - utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); - - } - else { - // 非异常模式下陷入 - utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe)); - } - // 检查异常栈是否溢出 - user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W); - - utf->utf_fault_va = fault_va; - utf->utf_err = tf->tf_trapno; - utf->utf_regs = tf->tf_regs; - utf->utf_eflags = tf->tf_eflags; - // 保存陷入时现场,用于返回 - utf->utf_eip = tf->tf_eip; - utf->utf_esp = tf->tf_esp; - // 再次转向执行 - curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall; - // 异常栈 - curenv->env_tf.tf_esp = (uint32_t) utf; - env_run(curenv); - } - else { - // Destroy the environment that caused the fault. - cprintf("[%08x] user fault va %08x ip %08x\n", - curenv->env_id, fault_va, tf->tf_eip); - print_trapframe(tf); - env_destroy(curenv); - } -} - diff --git a/lab/Untitled Project.si4project/Backup/trap(5971).c b/lab/Untitled Project.si4project/Backup/trap(5971).c deleted file mode 100644 index 96bf71c..0000000 --- a/lab/Untitled Project.si4project/Backup/trap(5971).c +++ /dev/null @@ -1,430 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct Taskstate ts; - -/* For debugging, so print_trapframe can distinguish between printing - * a saved trapframe and printing the current trapframe and print some - * additional information in the latter case. - */ -static struct Trapframe *last_tf; - -/* Interrupt descriptor table. (Must be built at run time because - * shifted function addresses can't be represented in relocation records.) - */ -struct Gatedesc idt[256] = { { 0 } }; -struct Pseudodesc idt_pd = { - sizeof(idt) - 1, (uint32_t) idt -}; - - -static const char *trapname(int trapno) -{ - static const char * const excnames[] = { - "Divide error", - "Debug", - "Non-Maskable Interrupt", - "Breakpoint", - "Overflow", - "BOUND Range Exceeded", - "Invalid Opcode", - "Device Not Available", - "Double Fault", - "Coprocessor Segment Overrun", - "Invalid TSS", - "Segment Not Present", - "Stack Fault", - "General Protection", - "Page Fault", - "(unknown trap)", - "x87 FPU Floating-Point Error", - "Alignment Check", - "Machine-Check", - "SIMD Floating-Point Exception" - }; - - if (trapno < ARRAY_SIZE(excnames)) - return excnames[trapno]; - if (trapno == T_SYSCALL) - return "System call"; - if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) - return "Hardware Interrupt"; - return "(unknown trap)"; -} - -// You will also need to modify trap_init() to initialize the idt to -// point to each of these entry points defined in trapentry.S; -// the SETGATE macro will be helpful here -void -trap_init(void) -{ - - extern struct Segdesc gdt[]; - void divide_handler(); - void debug_handler(); - void nmi_handler(); - void brkpt_handler(); - void oflow_handler(); - void bound_handler(); - void device_handler(); - void illop_handler(); - void tss_handler(); - void segnp_handler(); - void stack_handler(); - void gpflt_handler(); - void pgflt_handler(); - void fperr_handler(); - void align_handler(); - void mchk_handler(); - void simderr_handler(); - void syscall_handler(); - void dblflt_handler(); - void timer_handler(); - void kbd_handler(); - void serial_handler(); - void spurious_handler(); - void ide_handler(); - void error_handler(); - - - // LAB 3: Your code here. - // GD_KT 全局描述符, kernel text - SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); - SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); - SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); - SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); - SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); - SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); - SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); - SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); - SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); - SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); - SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); - SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); - SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); - SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); - SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); - SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); - SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); - SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); - SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); - // IRQ - SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_handler, 3); - - // Per-CPU setup - trap_init_percpu(); -} - -// Initialize and load the per-CPU TSS and IDT -void -trap_init_percpu(void) -{ - // The example code here sets up the Task State Segment (TSS) and - // the TSS descriptor for CPU 0. But it is incorrect if we are - // running on other CPUs because each CPU has its own kernel stack. - // Fix the code so that it works for all CPUs. - // - // Hints: - // - The macro "thiscpu" always refers to the current CPU's - // struct CpuInfo; - // - The ID of the current CPU is given by cpunum() or - // thiscpu->cpu_id; - // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, - // rather than the global "ts" variable; - // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; - // - You mapped the per-CPU kernel stacks in mem_init_mp() - // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments - // from doing IO (0 is not the correct value!) - // - // ltr sets a 'busy' flag in the TSS selector, so if you - // accidentally load the same TSS on more than one CPU, you'll - // get a triple fault. If you set up an individual CPU's TSS - // wrong, you may not get a fault until you try to return from - // user space on that CPU. - // - // LAB 4: Your code here: - thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); - thiscpu->cpu_ts.ts_ss0 = GD_KD; - - // Initialize the TSS slot of the gdt. - gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); - gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; - - // Load the TSS selector (like other segment selectors, the - // bottom three bits are special; we leave them 0) - ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); - - // Load the IDT - lidt(&idt_pd); -} - -void -print_trapframe(struct Trapframe *tf) -{ - cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); - print_regs(&tf->tf_regs); - cprintf(" es 0x----%04x\n", tf->tf_es); - cprintf(" ds 0x----%04x\n", tf->tf_ds); - cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); - // If this trap was a page fault that just happened - // (so %cr2 is meaningful), print the faulting linear address. - if (tf == last_tf && tf->tf_trapno == T_PGFLT) - cprintf(" cr2 0x%08x\n", rcr2()); - cprintf(" err 0x%08x", tf->tf_err); - // For page faults, print decoded fault error code: - // U/K=fault occurred in user/kernel mode - // W/R=a write/read caused the fault - // PR=a protection violation caused the fault (NP=page not present). - if (tf->tf_trapno == T_PGFLT) - cprintf(" [%s, %s, %s]\n", - tf->tf_err & 4 ? "user" : "kernel", - tf->tf_err & 2 ? "write" : "read", - tf->tf_err & 1 ? "protection" : "not-present"); - else - cprintf("\n"); - cprintf(" eip 0x%08x\n", tf->tf_eip); - cprintf(" cs 0x----%04x\n", tf->tf_cs); - cprintf(" flag 0x%08x\n", tf->tf_eflags); - if ((tf->tf_cs & 3) != 0) { - cprintf(" esp 0x%08x\n", tf->tf_esp); - cprintf(" ss 0x----%04x\n", tf->tf_ss); - } -} - -void -print_regs(struct PushRegs *regs) -{ - cprintf(" edi 0x%08x\n", regs->reg_edi); - cprintf(" esi 0x%08x\n", regs->reg_esi); - cprintf(" ebp 0x%08x\n", regs->reg_ebp); - cprintf(" oesp 0x%08x\n", regs->reg_oesp); - cprintf(" ebx 0x%08x\n", regs->reg_ebx); - cprintf(" edx 0x%08x\n", regs->reg_edx); - cprintf(" ecx 0x%08x\n", regs->reg_ecx); - cprintf(" eax 0x%08x\n", regs->reg_eax); -} - -static void -trap_dispatch(struct Trapframe *tf) -{ - // Handle processor exceptions. - // LAB 3: Your code here. - switch(tf->tf_trapno) { - case T_PGFLT: - page_fault_handler(tf); - break; - case T_BRKPT: - monitor(tf); - break; - case T_SYSCALL: - - tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, - tf->tf_regs.reg_edx, - tf->tf_regs.reg_ecx, - tf->tf_regs.reg_ebx, - tf->tf_regs.reg_edi, - tf->tf_regs.reg_esi); - break; - // Handle clock interrupts. Don't forget to acknowledge the - // interrupt using lapic_eoi() before calling the scheduler! - // LAB 4: Your code here. - case (IRQ_OFFSET + IRQ_TIMER): - // 回应8259A 接收中断。 - lapic_eoi(); - sched_yield(); - break; - - case (IRQ_OFFSET + IRQ_KBD): - lapic_eoi(); - kbd_intr(); - break; - case (IRQ_OFFSET + IRQ_SERIAL): - lapic_eoi(); - serial_intr(); - break; - - default: - // Unexpected trap: The user process or the kernel has a bug. - print_trapframe(tf); - if (tf->tf_cs == GD_KT) - panic("unhandled trap in kernel"); - else { - env_destroy(curenv); - return; - } - break; - - } - - -} - -void -trap(struct Trapframe *tf) -{ - // The environment may have set DF and some versions - // of GCC rely on DF being clear - asm volatile("cld" ::: "cc"); - - // Halt the CPU if some other CPU has called panic() - extern char *panicstr; - if (panicstr) - asm volatile("hlt"); - - // Re-acqurie the big kernel lock if we were halted in - // sched_yield() - if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) - lock_kernel(); - // Check that interrupts are disabled. If this assertion - // fails, DO NOT be tempted to fix it by inserting a "cli" in - // the interrupt path. - assert(!(read_eflags() & FL_IF)); - - if ((tf->tf_cs & 3) == 3) { - // Trapped from user mode. - // Acquire the big kernel lock before doing any - // serious kernel work. - // LAB 4: Your code here. - lock_kernel(); - assert(curenv); - - // Garbage collect if current enviroment is a zombie - if (curenv->env_status == ENV_DYING) { - env_free(curenv); - curenv = NULL; - sched_yield(); - } - - // Copy trap frame (which is currently on the stack) - // into 'curenv->env_tf', so that running the environment - // will restart at the trap point. - curenv->env_tf = *tf; - // The trapframe on the stack should be ignored from here on. - tf = &curenv->env_tf; - } - - // Record that tf is the last real trapframe so - // print_trapframe can print some additional information. - last_tf = tf; - - // Dispatch based on what type of trap occurred - trap_dispatch(tf); - - // If we made it to this point, then no other environment was - // scheduled, so we should return to the current environment - // if doing so makes sense. - if (curenv && curenv->env_status == ENV_RUNNING) - env_run(curenv); - else - sched_yield(); -} - - -void -page_fault_handler(struct Trapframe *tf) -{ - uint32_t fault_va; - - // Read processor's CR2 register to find the faulting address - fault_va = rcr2(); - - // Handle kernel-mode page faults. - - // LAB 3: Your code here. - - // 怎么判断是内核模式, CPL位 - - if(tf->tf_cs && 3 == 0) { - panic("page_fault in kernel mode, fault address %d\n", fault_va); - } - - // We've already handled kernel-mode exceptions, so if we get here, - // the page fault happened in user mode. - - - // Call the environment's page fault upcall, if one exists. Set up a - // page fault stack frame on the user exception stack (below - // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. - // - // The page fault upcall might cause another page fault, in which case - // we branch to the page fault upcall recursively, pushing another - // page fault stack frame on top of the user exception stack. - // - // It is convenient for our code which returns from a page fault - // (lib/pfentry.S) to have one word of scratch space at the top of the - // trap-time stack; it allows us to more easily restore the eip/esp. In - // the non-recursive case, we don't have to worry about this because - // the top of the regular user stack is free. In the recursive case, - // this means we have to leave an extra word between the current top of - // the exception stack and the new stack frame because the exception - // stack _is_ the trap-time stack. - // - // If there's no page fault upcall, the environment didn't allocate a - // page for its exception stack or can't write to it, or the exception - // stack overflows, then destroy the environment that caused the fault. - // Note that the grade script assumes you will first check for the page - // fault upcall and print the "user fault va" message below if there is - // none. The remaining three checks can be combined into a single test. - // - // Hints: - // user_mem_assert() and env_run() are useful here. - // To change what the user environment runs, modify 'curenv->env_tf' - // (the 'tf' variable points at 'curenv->env_tf'). - - // LAB 4: Your code here. - struct UTrapframe *utf; - // cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va); - if (curenv->env_pgfault_upcall) { - - if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { - // 异常模式下陷入 - utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); - - } - else { - // 非异常模式下陷入 - utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe)); - } - // 检查异常栈是否溢出 - user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W); - - utf->utf_fault_va = fault_va; - utf->utf_err = tf->tf_trapno; - utf->utf_regs = tf->tf_regs; - utf->utf_eflags = tf->tf_eflags; - // 保存陷入时现场,用于返回 - utf->utf_eip = tf->tf_eip; - utf->utf_esp = tf->tf_esp; - // 再次转向执行 - curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall; - // 异常栈 - curenv->env_tf.tf_esp = (uint32_t) utf; - env_run(curenv); - } - else { - // Destroy the environment that caused the fault. - cprintf("[%08x] user fault va %08x ip %08x\n", - curenv->env_id, fault_va, tf->tf_eip); - print_trapframe(tf); - env_destroy(curenv); - } -} - diff --git a/lab/Untitled Project.si4project/Backup/trap(6591).c b/lab/Untitled Project.si4project/Backup/trap(6591).c deleted file mode 100644 index b4153b6..0000000 --- a/lab/Untitled Project.si4project/Backup/trap(6591).c +++ /dev/null @@ -1,422 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct Taskstate ts; - -/* For debugging, so print_trapframe can distinguish between printing - * a saved trapframe and printing the current trapframe and print some - * additional information in the latter case. - */ -static struct Trapframe *last_tf; - -/* Interrupt descriptor table. (Must be built at run time because - * shifted function addresses can't be represented in relocation records.) - */ -struct Gatedesc idt[256] = { { 0 } }; -struct Pseudodesc idt_pd = { - sizeof(idt) - 1, (uint32_t) idt -}; - - -static const char *trapname(int trapno) -{ - static const char * const excnames[] = { - "Divide error", - "Debug", - "Non-Maskable Interrupt", - "Breakpoint", - "Overflow", - "BOUND Range Exceeded", - "Invalid Opcode", - "Device Not Available", - "Double Fault", - "Coprocessor Segment Overrun", - "Invalid TSS", - "Segment Not Present", - "Stack Fault", - "General Protection", - "Page Fault", - "(unknown trap)", - "x87 FPU Floating-Point Error", - "Alignment Check", - "Machine-Check", - "SIMD Floating-Point Exception" - }; - - if (trapno < ARRAY_SIZE(excnames)) - return excnames[trapno]; - if (trapno == T_SYSCALL) - return "System call"; - if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) - return "Hardware Interrupt"; - return "(unknown trap)"; -} - -// You will also need to modify trap_init() to initialize the idt to -// point to each of these entry points defined in trapentry.S; -// the SETGATE macro will be helpful here -void -trap_init(void) -{ - - extern struct Segdesc gdt[]; - void divide_handler(); - void debug_handler(); - void nmi_handler(); - void brkpt_handler(); - void oflow_handler(); - void bound_handler(); - void device_handler(); - void illop_handler(); - void tss_handler(); - void segnp_handler(); - void stack_handler(); - void gpflt_handler(); - void pgflt_handler(); - void fperr_handler(); - void align_handler(); - void mchk_handler(); - void simderr_handler(); - void syscall_handler(); - void dblflt_handler(); - void timer_handler(); - void kbd_handler(); - void serial_handler(); - void spurious_handler(); - void ide_handler(); - void error_handler(); - - - // LAB 3: Your code here. - // GD_KT 全局描述符, kernel text - SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); - SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); - SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); - SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); - SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); - SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); - SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); - SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); - SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); - SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); - SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); - SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); - SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); - SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); - SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); - SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); - SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); - SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); - SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); - // IRQ - SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_handler, 3); - - // Per-CPU setup - trap_init_percpu(); -} - -// Initialize and load the per-CPU TSS and IDT -void -trap_init_percpu(void) -{ - // The example code here sets up the Task State Segment (TSS) and - // the TSS descriptor for CPU 0. But it is incorrect if we are - // running on other CPUs because each CPU has its own kernel stack. - // Fix the code so that it works for all CPUs. - // - // Hints: - // - The macro "thiscpu" always refers to the current CPU's - // struct CpuInfo; - // - The ID of the current CPU is given by cpunum() or - // thiscpu->cpu_id; - // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, - // rather than the global "ts" variable; - // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; - // - You mapped the per-CPU kernel stacks in mem_init_mp() - // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments - // from doing IO (0 is not the correct value!) - // - // ltr sets a 'busy' flag in the TSS selector, so if you - // accidentally load the same TSS on more than one CPU, you'll - // get a triple fault. If you set up an individual CPU's TSS - // wrong, you may not get a fault until you try to return from - // user space on that CPU. - // - // LAB 4: Your code here: - thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); - thiscpu->cpu_ts.ts_ss0 = GD_KD; - - // Initialize the TSS slot of the gdt. - gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); - gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; - - // Load the TSS selector (like other segment selectors, the - // bottom three bits are special; we leave them 0) - ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); - - // Load the IDT - lidt(&idt_pd); -} - -void -print_trapframe(struct Trapframe *tf) -{ - cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); - print_regs(&tf->tf_regs); - cprintf(" es 0x----%04x\n", tf->tf_es); - cprintf(" ds 0x----%04x\n", tf->tf_ds); - cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); - // If this trap was a page fault that just happened - // (so %cr2 is meaningful), print the faulting linear address. - if (tf == last_tf && tf->tf_trapno == T_PGFLT) - cprintf(" cr2 0x%08x\n", rcr2()); - cprintf(" err 0x%08x", tf->tf_err); - // For page faults, print decoded fault error code: - // U/K=fault occurred in user/kernel mode - // W/R=a write/read caused the fault - // PR=a protection violation caused the fault (NP=page not present). - if (tf->tf_trapno == T_PGFLT) - cprintf(" [%s, %s, %s]\n", - tf->tf_err & 4 ? "user" : "kernel", - tf->tf_err & 2 ? "write" : "read", - tf->tf_err & 1 ? "protection" : "not-present"); - else - cprintf("\n"); - cprintf(" eip 0x%08x\n", tf->tf_eip); - cprintf(" cs 0x----%04x\n", tf->tf_cs); - cprintf(" flag 0x%08x\n", tf->tf_eflags); - if ((tf->tf_cs & 3) != 0) { - cprintf(" esp 0x%08x\n", tf->tf_esp); - cprintf(" ss 0x----%04x\n", tf->tf_ss); - } -} - -void -print_regs(struct PushRegs *regs) -{ - cprintf(" edi 0x%08x\n", regs->reg_edi); - cprintf(" esi 0x%08x\n", regs->reg_esi); - cprintf(" ebp 0x%08x\n", regs->reg_ebp); - cprintf(" oesp 0x%08x\n", regs->reg_oesp); - cprintf(" ebx 0x%08x\n", regs->reg_ebx); - cprintf(" edx 0x%08x\n", regs->reg_edx); - cprintf(" ecx 0x%08x\n", regs->reg_ecx); - cprintf(" eax 0x%08x\n", regs->reg_eax); -} - -static void -trap_dispatch(struct Trapframe *tf) -{ - // Handle processor exceptions. - // LAB 3: Your code here. - switch(tf->tf_trapno) { - case T_PGFLT: - page_fault_handler(tf); - break; - case T_BRKPT: - monitor(tf); - break; - case T_SYSCALL: - - tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, - tf->tf_regs.reg_edx, - tf->tf_regs.reg_ecx, - tf->tf_regs.reg_ebx, - tf->tf_regs.reg_edi, - tf->tf_regs.reg_esi); - break; - case (IRQ_OFFSET + IRQ_TIMER): - // 回应8259A 接收中断。 - lapic_eoi(); - sched_yield(); - break; - - default: - // Unexpected trap: The user process or the kernel has a bug. - print_trapframe(tf); - if (tf->tf_cs == GD_KT) - panic("unhandled trap in kernel"); - else { - env_destroy(curenv); - return; - } - break; - - } - - // Handle clock interrupts. Don't forget to acknowledge the - // interrupt using lapic_eoi() before calling the scheduler! - // LAB 4: Your code here. - - -} - -void -trap(struct Trapframe *tf) -{ - // The environment may have set DF and some versions - // of GCC rely on DF being clear - asm volatile("cld" ::: "cc"); - - // Halt the CPU if some other CPU has called panic() - extern char *panicstr; - if (panicstr) - asm volatile("hlt"); - - // Re-acqurie the big kernel lock if we were halted in - // sched_yield() - if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) - lock_kernel(); - // Check that interrupts are disabled. If this assertion - // fails, DO NOT be tempted to fix it by inserting a "cli" in - // the interrupt path. - assert(!(read_eflags() & FL_IF)); - - if ((tf->tf_cs & 3) == 3) { - // Trapped from user mode. - // Acquire the big kernel lock before doing any - // serious kernel work. - // LAB 4: Your code here. - lock_kernel(); - assert(curenv); - - // Garbage collect if current enviroment is a zombie - if (curenv->env_status == ENV_DYING) { - env_free(curenv); - curenv = NULL; - sched_yield(); - } - - // Copy trap frame (which is currently on the stack) - // into 'curenv->env_tf', so that running the environment - // will restart at the trap point. - curenv->env_tf = *tf; - // The trapframe on the stack should be ignored from here on. - tf = &curenv->env_tf; - } - - // Record that tf is the last real trapframe so - // print_trapframe can print some additional information. - last_tf = tf; - - // Dispatch based on what type of trap occurred - trap_dispatch(tf); - - // If we made it to this point, then no other environment was - // scheduled, so we should return to the current environment - // if doing so makes sense. - if (curenv && curenv->env_status == ENV_RUNNING) - env_run(curenv); - else - sched_yield(); -} - - -void -page_fault_handler(struct Trapframe *tf) -{ - uint32_t fault_va; - - // Read processor's CR2 register to find the faulting address - fault_va = rcr2(); - - // Handle kernel-mode page faults. - - // LAB 3: Your code here. - - // 怎么判断是内核模式, CPL位 - - if(tf->tf_cs && 3 == 0) { - panic("page_fault in kernel mode, fault address %d\n", fault_va); - } - - // We've already handled kernel-mode exceptions, so if we get here, - // the page fault happened in user mode. - - - // Call the environment's page fault upcall, if one exists. Set up a - // page fault stack frame on the user exception stack (below - // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. - // - // The page fault upcall might cause another page fault, in which case - // we branch to the page fault upcall recursively, pushing another - // page fault stack frame on top of the user exception stack. - // - // It is convenient for our code which returns from a page fault - // (lib/pfentry.S) to have one word of scratch space at the top of the - // trap-time stack; it allows us to more easily restore the eip/esp. In - // the non-recursive case, we don't have to worry about this because - // the top of the regular user stack is free. In the recursive case, - // this means we have to leave an extra word between the current top of - // the exception stack and the new stack frame because the exception - // stack _is_ the trap-time stack. - // - // If there's no page fault upcall, the environment didn't allocate a - // page for its exception stack or can't write to it, or the exception - // stack overflows, then destroy the environment that caused the fault. - // Note that the grade script assumes you will first check for the page - // fault upcall and print the "user fault va" message below if there is - // none. The remaining three checks can be combined into a single test. - // - // Hints: - // user_mem_assert() and env_run() are useful here. - // To change what the user environment runs, modify 'curenv->env_tf' - // (the 'tf' variable points at 'curenv->env_tf'). - - // LAB 4: Your code here. - struct UTrapframe *utf; - // cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va); - if (curenv->env_pgfault_upcall) { - - if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { - // 异常模式下陷入 - utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); - - } - else { - // 非异常模式下陷入 - utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe)); - } - // 检查异常栈是否溢出 - user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W); - - utf->utf_fault_va = fault_va; - utf->utf_err = tf->tf_trapno; - utf->utf_regs = tf->tf_regs; - utf->utf_eflags = tf->tf_eflags; - // 保存陷入时现场,用于返回 - utf->utf_eip = tf->tf_eip; - utf->utf_esp = tf->tf_esp; - // 再次转向执行 - curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall; - // 异常栈 - curenv->env_tf.tf_esp = (uint32_t) utf; - env_run(curenv); - } - else { - // Destroy the environment that caused the fault. - cprintf("[%08x] user fault va %08x ip %08x\n", - curenv->env_id, fault_va, tf->tf_eip); - print_trapframe(tf); - env_destroy(curenv); - } -} - diff --git a/lab/Untitled Project.si4project/Backup/trap(6593).c b/lab/Untitled Project.si4project/Backup/trap(6593).c deleted file mode 100644 index 141c7df..0000000 --- a/lab/Untitled Project.si4project/Backup/trap(6593).c +++ /dev/null @@ -1,421 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct Taskstate ts; - -/* For debugging, so print_trapframe can distinguish between printing - * a saved trapframe and printing the current trapframe and print some - * additional information in the latter case. - */ -static struct Trapframe *last_tf; - -/* Interrupt descriptor table. (Must be built at run time because - * shifted function addresses can't be represented in relocation records.) - */ -struct Gatedesc idt[256] = { { 0 } }; -struct Pseudodesc idt_pd = { - sizeof(idt) - 1, (uint32_t) idt -}; - - -static const char *trapname(int trapno) -{ - static const char * const excnames[] = { - "Divide error", - "Debug", - "Non-Maskable Interrupt", - "Breakpoint", - "Overflow", - "BOUND Range Exceeded", - "Invalid Opcode", - "Device Not Available", - "Double Fault", - "Coprocessor Segment Overrun", - "Invalid TSS", - "Segment Not Present", - "Stack Fault", - "General Protection", - "Page Fault", - "(unknown trap)", - "x87 FPU Floating-Point Error", - "Alignment Check", - "Machine-Check", - "SIMD Floating-Point Exception" - }; - - if (trapno < ARRAY_SIZE(excnames)) - return excnames[trapno]; - if (trapno == T_SYSCALL) - return "System call"; - if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) - return "Hardware Interrupt"; - return "(unknown trap)"; -} - -// You will also need to modify trap_init() to initialize the idt to -// point to each of these entry points defined in trapentry.S; -// the SETGATE macro will be helpful here -void -trap_init(void) -{ - - extern struct Segdesc gdt[]; - void divide_handler(); - void debug_handler(); - void nmi_handler(); - void brkpt_handler(); - void oflow_handler(); - void bound_handler(); - void device_handler(); - void illop_handler(); - void tss_handler(); - void segnp_handler(); - void stack_handler(); - void gpflt_handler(); - void pgflt_handler(); - void fperr_handler(); - void align_handler(); - void mchk_handler(); - void simderr_handler(); - void syscall_handler(); - void dblflt_handler(); - void timer_handler(); - void kbd_handler(); - void serial_handler(); - void spurious_handler(); - void ide_handler(); - void error_handler(); - - - // LAB 3: Your code here. - // GD_KT 全局描述符, kernel text - SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); - SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); - SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); - SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); - SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); - SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); - SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); - SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); - SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); - SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); - SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); - SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); - SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); - SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); - SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); - SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); - SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); - SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); - SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); - // IRQ - SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_handler, 3); - - // Per-CPU setup - trap_init_percpu(); -} - -// Initialize and load the per-CPU TSS and IDT -void -trap_init_percpu(void) -{ - // The example code here sets up the Task State Segment (TSS) and - // the TSS descriptor for CPU 0. But it is incorrect if we are - // running on other CPUs because each CPU has its own kernel stack. - // Fix the code so that it works for all CPUs. - // - // Hints: - // - The macro "thiscpu" always refers to the current CPU's - // struct CpuInfo; - // - The ID of the current CPU is given by cpunum() or - // thiscpu->cpu_id; - // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, - // rather than the global "ts" variable; - // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; - // - You mapped the per-CPU kernel stacks in mem_init_mp() - // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments - // from doing IO (0 is not the correct value!) - // - // ltr sets a 'busy' flag in the TSS selector, so if you - // accidentally load the same TSS on more than one CPU, you'll - // get a triple fault. If you set up an individual CPU's TSS - // wrong, you may not get a fault until you try to return from - // user space on that CPU. - // - // LAB 4: Your code here: - thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); - thiscpu->cpu_ts.ts_ss0 = GD_KD; - - // Initialize the TSS slot of the gdt. - gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); - gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; - - // Load the TSS selector (like other segment selectors, the - // bottom three bits are special; we leave them 0) - ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); - - // Load the IDT - lidt(&idt_pd); -} - -void -print_trapframe(struct Trapframe *tf) -{ - cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); - print_regs(&tf->tf_regs); - cprintf(" es 0x----%04x\n", tf->tf_es); - cprintf(" ds 0x----%04x\n", tf->tf_ds); - cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); - // If this trap was a page fault that just happened - // (so %cr2 is meaningful), print the faulting linear address. - if (tf == last_tf && tf->tf_trapno == T_PGFLT) - cprintf(" cr2 0x%08x\n", rcr2()); - cprintf(" err 0x%08x", tf->tf_err); - // For page faults, print decoded fault error code: - // U/K=fault occurred in user/kernel mode - // W/R=a write/read caused the fault - // PR=a protection violation caused the fault (NP=page not present). - if (tf->tf_trapno == T_PGFLT) - cprintf(" [%s, %s, %s]\n", - tf->tf_err & 4 ? "user" : "kernel", - tf->tf_err & 2 ? "write" : "read", - tf->tf_err & 1 ? "protection" : "not-present"); - else - cprintf("\n"); - cprintf(" eip 0x%08x\n", tf->tf_eip); - cprintf(" cs 0x----%04x\n", tf->tf_cs); - cprintf(" flag 0x%08x\n", tf->tf_eflags); - if ((tf->tf_cs & 3) != 0) { - cprintf(" esp 0x%08x\n", tf->tf_esp); - cprintf(" ss 0x----%04x\n", tf->tf_ss); - } -} - -void -print_regs(struct PushRegs *regs) -{ - cprintf(" edi 0x%08x\n", regs->reg_edi); - cprintf(" esi 0x%08x\n", regs->reg_esi); - cprintf(" ebp 0x%08x\n", regs->reg_ebp); - cprintf(" oesp 0x%08x\n", regs->reg_oesp); - cprintf(" ebx 0x%08x\n", regs->reg_ebx); - cprintf(" edx 0x%08x\n", regs->reg_edx); - cprintf(" ecx 0x%08x\n", regs->reg_ecx); - cprintf(" eax 0x%08x\n", regs->reg_eax); -} - -static void -trap_dispatch(struct Trapframe *tf) -{ - // Handle processor exceptions. - // LAB 3: Your code here. - switch(tf->tf_trapno) { - case T_PGFLT: - page_fault_handler(tf); - break; - case T_BRKPT: - monitor(tf); - break; - case T_SYSCALL: - - tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, - tf->tf_regs.reg_edx, - tf->tf_regs.reg_ecx, - tf->tf_regs.reg_ebx, - tf->tf_regs.reg_edi, - tf->tf_regs.reg_esi); - break; - // Handle clock interrupts. Don't forget to acknowledge the - // interrupt using lapic_eoi() before calling the scheduler! - // LAB 4: Your code here. - case (IRQ_OFFSET + IRQ_TIMER): - // 回应8259A 接收中断。 - lapic_eoi(); - sched_yield(); - break; - - default: - // Unexpected trap: The user process or the kernel has a bug. - print_trapframe(tf); - if (tf->tf_cs == GD_KT) - panic("unhandled trap in kernel"); - else { - env_destroy(curenv); - return; - } - break; - - } - - -} - -void -trap(struct Trapframe *tf) -{ - // The environment may have set DF and some versions - // of GCC rely on DF being clear - asm volatile("cld" ::: "cc"); - - // Halt the CPU if some other CPU has called panic() - extern char *panicstr; - if (panicstr) - asm volatile("hlt"); - - // Re-acqurie the big kernel lock if we were halted in - // sched_yield() - if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) - lock_kernel(); - // Check that interrupts are disabled. If this assertion - // fails, DO NOT be tempted to fix it by inserting a "cli" in - // the interrupt path. - assert(!(read_eflags() & FL_IF)); - - if ((tf->tf_cs & 3) == 3) { - // Trapped from user mode. - // Acquire the big kernel lock before doing any - // serious kernel work. - // LAB 4: Your code here. - lock_kernel(); - assert(curenv); - - // Garbage collect if current enviroment is a zombie - if (curenv->env_status == ENV_DYING) { - env_free(curenv); - curenv = NULL; - sched_yield(); - } - - // Copy trap frame (which is currently on the stack) - // into 'curenv->env_tf', so that running the environment - // will restart at the trap point. - curenv->env_tf = *tf; - // The trapframe on the stack should be ignored from here on. - tf = &curenv->env_tf; - } - - // Record that tf is the last real trapframe so - // print_trapframe can print some additional information. - last_tf = tf; - - // Dispatch based on what type of trap occurred - trap_dispatch(tf); - - // If we made it to this point, then no other environment was - // scheduled, so we should return to the current environment - // if doing so makes sense. - if (curenv && curenv->env_status == ENV_RUNNING) - env_run(curenv); - else - sched_yield(); -} - - -void -page_fault_handler(struct Trapframe *tf) -{ - uint32_t fault_va; - - // Read processor's CR2 register to find the faulting address - fault_va = rcr2(); - - // Handle kernel-mode page faults. - - // LAB 3: Your code here. - - // 怎么判断是内核模式, CPL位 - - if(tf->tf_cs && 3 == 0) { - panic("page_fault in kernel mode, fault address %d\n", fault_va); - } - - // We've already handled kernel-mode exceptions, so if we get here, - // the page fault happened in user mode. - - - // Call the environment's page fault upcall, if one exists. Set up a - // page fault stack frame on the user exception stack (below - // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. - // - // The page fault upcall might cause another page fault, in which case - // we branch to the page fault upcall recursively, pushing another - // page fault stack frame on top of the user exception stack. - // - // It is convenient for our code which returns from a page fault - // (lib/pfentry.S) to have one word of scratch space at the top of the - // trap-time stack; it allows us to more easily restore the eip/esp. In - // the non-recursive case, we don't have to worry about this because - // the top of the regular user stack is free. In the recursive case, - // this means we have to leave an extra word between the current top of - // the exception stack and the new stack frame because the exception - // stack _is_ the trap-time stack. - // - // If there's no page fault upcall, the environment didn't allocate a - // page for its exception stack or can't write to it, or the exception - // stack overflows, then destroy the environment that caused the fault. - // Note that the grade script assumes you will first check for the page - // fault upcall and print the "user fault va" message below if there is - // none. The remaining three checks can be combined into a single test. - // - // Hints: - // user_mem_assert() and env_run() are useful here. - // To change what the user environment runs, modify 'curenv->env_tf' - // (the 'tf' variable points at 'curenv->env_tf'). - - // LAB 4: Your code here. - struct UTrapframe *utf; - // cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va); - if (curenv->env_pgfault_upcall) { - - if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { - // 异常模式下陷入 - utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); - - } - else { - // 非异常模式下陷入 - utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe)); - } - // 检查异常栈是否溢出 - user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W); - - utf->utf_fault_va = fault_va; - utf->utf_err = tf->tf_trapno; - utf->utf_regs = tf->tf_regs; - utf->utf_eflags = tf->tf_eflags; - // 保存陷入时现场,用于返回 - utf->utf_eip = tf->tf_eip; - utf->utf_esp = tf->tf_esp; - // 再次转向执行 - curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall; - // 异常栈 - curenv->env_tf.tf_esp = (uint32_t) utf; - env_run(curenv); - } - else { - // Destroy the environment that caused the fault. - cprintf("[%08x] user fault va %08x ip %08x\n", - curenv->env_id, fault_va, tf->tf_eip); - print_trapframe(tf); - env_destroy(curenv); - } -} - diff --git a/lab/Untitled Project.si4project/Backup/trap(7420).c b/lab/Untitled Project.si4project/Backup/trap(7420).c deleted file mode 100644 index 96bf71c..0000000 --- a/lab/Untitled Project.si4project/Backup/trap(7420).c +++ /dev/null @@ -1,430 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct Taskstate ts; - -/* For debugging, so print_trapframe can distinguish between printing - * a saved trapframe and printing the current trapframe and print some - * additional information in the latter case. - */ -static struct Trapframe *last_tf; - -/* Interrupt descriptor table. (Must be built at run time because - * shifted function addresses can't be represented in relocation records.) - */ -struct Gatedesc idt[256] = { { 0 } }; -struct Pseudodesc idt_pd = { - sizeof(idt) - 1, (uint32_t) idt -}; - - -static const char *trapname(int trapno) -{ - static const char * const excnames[] = { - "Divide error", - "Debug", - "Non-Maskable Interrupt", - "Breakpoint", - "Overflow", - "BOUND Range Exceeded", - "Invalid Opcode", - "Device Not Available", - "Double Fault", - "Coprocessor Segment Overrun", - "Invalid TSS", - "Segment Not Present", - "Stack Fault", - "General Protection", - "Page Fault", - "(unknown trap)", - "x87 FPU Floating-Point Error", - "Alignment Check", - "Machine-Check", - "SIMD Floating-Point Exception" - }; - - if (trapno < ARRAY_SIZE(excnames)) - return excnames[trapno]; - if (trapno == T_SYSCALL) - return "System call"; - if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) - return "Hardware Interrupt"; - return "(unknown trap)"; -} - -// You will also need to modify trap_init() to initialize the idt to -// point to each of these entry points defined in trapentry.S; -// the SETGATE macro will be helpful here -void -trap_init(void) -{ - - extern struct Segdesc gdt[]; - void divide_handler(); - void debug_handler(); - void nmi_handler(); - void brkpt_handler(); - void oflow_handler(); - void bound_handler(); - void device_handler(); - void illop_handler(); - void tss_handler(); - void segnp_handler(); - void stack_handler(); - void gpflt_handler(); - void pgflt_handler(); - void fperr_handler(); - void align_handler(); - void mchk_handler(); - void simderr_handler(); - void syscall_handler(); - void dblflt_handler(); - void timer_handler(); - void kbd_handler(); - void serial_handler(); - void spurious_handler(); - void ide_handler(); - void error_handler(); - - - // LAB 3: Your code here. - // GD_KT 全局描述符, kernel text - SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); - SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); - SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); - SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); - SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); - SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); - SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); - SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); - SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); - SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); - SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); - SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); - SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); - SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); - SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); - SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); - SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); - SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); - SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); - // IRQ - SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3); - SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_handler, 3); - - // Per-CPU setup - trap_init_percpu(); -} - -// Initialize and load the per-CPU TSS and IDT -void -trap_init_percpu(void) -{ - // The example code here sets up the Task State Segment (TSS) and - // the TSS descriptor for CPU 0. But it is incorrect if we are - // running on other CPUs because each CPU has its own kernel stack. - // Fix the code so that it works for all CPUs. - // - // Hints: - // - The macro "thiscpu" always refers to the current CPU's - // struct CpuInfo; - // - The ID of the current CPU is given by cpunum() or - // thiscpu->cpu_id; - // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, - // rather than the global "ts" variable; - // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; - // - You mapped the per-CPU kernel stacks in mem_init_mp() - // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments - // from doing IO (0 is not the correct value!) - // - // ltr sets a 'busy' flag in the TSS selector, so if you - // accidentally load the same TSS on more than one CPU, you'll - // get a triple fault. If you set up an individual CPU's TSS - // wrong, you may not get a fault until you try to return from - // user space on that CPU. - // - // LAB 4: Your code here: - thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); - thiscpu->cpu_ts.ts_ss0 = GD_KD; - - // Initialize the TSS slot of the gdt. - gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); - gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; - - // Load the TSS selector (like other segment selectors, the - // bottom three bits are special; we leave them 0) - ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); - - // Load the IDT - lidt(&idt_pd); -} - -void -print_trapframe(struct Trapframe *tf) -{ - cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); - print_regs(&tf->tf_regs); - cprintf(" es 0x----%04x\n", tf->tf_es); - cprintf(" ds 0x----%04x\n", tf->tf_ds); - cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); - // If this trap was a page fault that just happened - // (so %cr2 is meaningful), print the faulting linear address. - if (tf == last_tf && tf->tf_trapno == T_PGFLT) - cprintf(" cr2 0x%08x\n", rcr2()); - cprintf(" err 0x%08x", tf->tf_err); - // For page faults, print decoded fault error code: - // U/K=fault occurred in user/kernel mode - // W/R=a write/read caused the fault - // PR=a protection violation caused the fault (NP=page not present). - if (tf->tf_trapno == T_PGFLT) - cprintf(" [%s, %s, %s]\n", - tf->tf_err & 4 ? "user" : "kernel", - tf->tf_err & 2 ? "write" : "read", - tf->tf_err & 1 ? "protection" : "not-present"); - else - cprintf("\n"); - cprintf(" eip 0x%08x\n", tf->tf_eip); - cprintf(" cs 0x----%04x\n", tf->tf_cs); - cprintf(" flag 0x%08x\n", tf->tf_eflags); - if ((tf->tf_cs & 3) != 0) { - cprintf(" esp 0x%08x\n", tf->tf_esp); - cprintf(" ss 0x----%04x\n", tf->tf_ss); - } -} - -void -print_regs(struct PushRegs *regs) -{ - cprintf(" edi 0x%08x\n", regs->reg_edi); - cprintf(" esi 0x%08x\n", regs->reg_esi); - cprintf(" ebp 0x%08x\n", regs->reg_ebp); - cprintf(" oesp 0x%08x\n", regs->reg_oesp); - cprintf(" ebx 0x%08x\n", regs->reg_ebx); - cprintf(" edx 0x%08x\n", regs->reg_edx); - cprintf(" ecx 0x%08x\n", regs->reg_ecx); - cprintf(" eax 0x%08x\n", regs->reg_eax); -} - -static void -trap_dispatch(struct Trapframe *tf) -{ - // Handle processor exceptions. - // LAB 3: Your code here. - switch(tf->tf_trapno) { - case T_PGFLT: - page_fault_handler(tf); - break; - case T_BRKPT: - monitor(tf); - break; - case T_SYSCALL: - - tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, - tf->tf_regs.reg_edx, - tf->tf_regs.reg_ecx, - tf->tf_regs.reg_ebx, - tf->tf_regs.reg_edi, - tf->tf_regs.reg_esi); - break; - // Handle clock interrupts. Don't forget to acknowledge the - // interrupt using lapic_eoi() before calling the scheduler! - // LAB 4: Your code here. - case (IRQ_OFFSET + IRQ_TIMER): - // 回应8259A 接收中断。 - lapic_eoi(); - sched_yield(); - break; - - case (IRQ_OFFSET + IRQ_KBD): - lapic_eoi(); - kbd_intr(); - break; - case (IRQ_OFFSET + IRQ_SERIAL): - lapic_eoi(); - serial_intr(); - break; - - default: - // Unexpected trap: The user process or the kernel has a bug. - print_trapframe(tf); - if (tf->tf_cs == GD_KT) - panic("unhandled trap in kernel"); - else { - env_destroy(curenv); - return; - } - break; - - } - - -} - -void -trap(struct Trapframe *tf) -{ - // The environment may have set DF and some versions - // of GCC rely on DF being clear - asm volatile("cld" ::: "cc"); - - // Halt the CPU if some other CPU has called panic() - extern char *panicstr; - if (panicstr) - asm volatile("hlt"); - - // Re-acqurie the big kernel lock if we were halted in - // sched_yield() - if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) - lock_kernel(); - // Check that interrupts are disabled. If this assertion - // fails, DO NOT be tempted to fix it by inserting a "cli" in - // the interrupt path. - assert(!(read_eflags() & FL_IF)); - - if ((tf->tf_cs & 3) == 3) { - // Trapped from user mode. - // Acquire the big kernel lock before doing any - // serious kernel work. - // LAB 4: Your code here. - lock_kernel(); - assert(curenv); - - // Garbage collect if current enviroment is a zombie - if (curenv->env_status == ENV_DYING) { - env_free(curenv); - curenv = NULL; - sched_yield(); - } - - // Copy trap frame (which is currently on the stack) - // into 'curenv->env_tf', so that running the environment - // will restart at the trap point. - curenv->env_tf = *tf; - // The trapframe on the stack should be ignored from here on. - tf = &curenv->env_tf; - } - - // Record that tf is the last real trapframe so - // print_trapframe can print some additional information. - last_tf = tf; - - // Dispatch based on what type of trap occurred - trap_dispatch(tf); - - // If we made it to this point, then no other environment was - // scheduled, so we should return to the current environment - // if doing so makes sense. - if (curenv && curenv->env_status == ENV_RUNNING) - env_run(curenv); - else - sched_yield(); -} - - -void -page_fault_handler(struct Trapframe *tf) -{ - uint32_t fault_va; - - // Read processor's CR2 register to find the faulting address - fault_va = rcr2(); - - // Handle kernel-mode page faults. - - // LAB 3: Your code here. - - // 怎么判断是内核模式, CPL位 - - if(tf->tf_cs && 3 == 0) { - panic("page_fault in kernel mode, fault address %d\n", fault_va); - } - - // We've already handled kernel-mode exceptions, so if we get here, - // the page fault happened in user mode. - - - // Call the environment's page fault upcall, if one exists. Set up a - // page fault stack frame on the user exception stack (below - // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. - // - // The page fault upcall might cause another page fault, in which case - // we branch to the page fault upcall recursively, pushing another - // page fault stack frame on top of the user exception stack. - // - // It is convenient for our code which returns from a page fault - // (lib/pfentry.S) to have one word of scratch space at the top of the - // trap-time stack; it allows us to more easily restore the eip/esp. In - // the non-recursive case, we don't have to worry about this because - // the top of the regular user stack is free. In the recursive case, - // this means we have to leave an extra word between the current top of - // the exception stack and the new stack frame because the exception - // stack _is_ the trap-time stack. - // - // If there's no page fault upcall, the environment didn't allocate a - // page for its exception stack or can't write to it, or the exception - // stack overflows, then destroy the environment that caused the fault. - // Note that the grade script assumes you will first check for the page - // fault upcall and print the "user fault va" message below if there is - // none. The remaining three checks can be combined into a single test. - // - // Hints: - // user_mem_assert() and env_run() are useful here. - // To change what the user environment runs, modify 'curenv->env_tf' - // (the 'tf' variable points at 'curenv->env_tf'). - - // LAB 4: Your code here. - struct UTrapframe *utf; - // cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va); - if (curenv->env_pgfault_upcall) { - - if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { - // 异常模式下陷入 - utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); - - } - else { - // 非异常模式下陷入 - utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe)); - } - // 检查异常栈是否溢出 - user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W); - - utf->utf_fault_va = fault_va; - utf->utf_err = tf->tf_trapno; - utf->utf_regs = tf->tf_regs; - utf->utf_eflags = tf->tf_eflags; - // 保存陷入时现场,用于返回 - utf->utf_eip = tf->tf_eip; - utf->utf_esp = tf->tf_esp; - // 再次转向执行 - curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall; - // 异常栈 - curenv->env_tf.tf_esp = (uint32_t) utf; - env_run(curenv); - } - else { - // Destroy the environment that caused the fault. - cprintf("[%08x] user fault va %08x ip %08x\n", - curenv->env_id, fault_va, tf->tf_eip); - print_trapframe(tf); - env_destroy(curenv); - } -} - diff --git a/lab/Untitled Project.si4project/Untitled Project.SearchResults b/lab/Untitled Project.si4project/Untitled Project.SearchResults deleted file mode 100644 index 943d2e3..0000000 --- a/lab/Untitled Project.si4project/Untitled Project.SearchResults +++ /dev/null @@ -1,3 +0,0 @@ ----- IOPL Matches (2 in 2 files) ---- -env_create in env.c (kern) : newenv->env_tf.tf_eflags |= IOPL -syscall.c (kern) line 134 : // protection level 3 (CPL 3), interrupts enabled, and IOPL of 0. diff --git a/lab/Untitled Project.si4project/Untitled Project.sip_sym b/lab/Untitled Project.si4project/Untitled Project.sip_sym deleted file mode 100644 index 291c4c230161787551b46c99022faa98cf9cfe11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 305432 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�AEEU$o!k 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 b9nZ?DbvJbj%p<`)%`l!{s=%Tt@%;Y*c@seO diff --git a/lab/Untitled Project.si4project/Untitled Project.sip_xab b/lab/Untitled Project.si4project/Untitled Project.sip_xab deleted file mode 100644 index df7c68aaef5229cbf00e67dffc8b5a107ef7c5d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69632 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 diff --git a/lab/Untitled Project.si4project/Untitled Project.sip_xad b/lab/Untitled Project.si4project/Untitled Project.sip_xad deleted file mode 100644 index c715f24ccd0c62cf3f31eba9d321f0a6b7bd2503..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 zcmc(eu}eZx6vmHcicnw>5w$oJM3ZxqB^r^K_-u(F#iUx&%$6W|5Dh_rw6sLHHEPfr zNK>Oh&}v&t;7~0=^nK^P==}#Cc;EZ(xxaVsx$m8KF)ET3@rg(J8X@feJkZw?bn_7? zuNhn)sA>GO8R(tg1vpvoil6r(5qmyHCvvQM6x5p|hd#skFkV<$O)-5+iGqvLm{=F_ z(t)#M459dmf>>8da3)c3%t z9JriesFv$Jr{X5B=O}|K|8vtQ%r6tVZ#uuF=Qz>n8oq~9JjQdlf--BkN~u8Ponm7> zo5sYRYu`*lmYLP_*1^>s2)Ap~BD$WnEqdM?PB*|cXiTg*CGfpi)sj}O%k;qY{wR?t zkNF<>742-?!tW?@Noj#=(U^GhYHMoKRQEUZy!V`HgKHbct^986;WzZ~<5yFA_!m|o BR2~2T diff --git a/lab/Untitled Project.si4project/Untitled Project.sip_xm b/lab/Untitled Project.si4project/Untitled Project.sip_xm deleted file mode 100644 index a41b397674024eed2795f09f3824b1ff2d4fb69c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 540 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 LE03@1!EpruhfXkk diff --git a/lab/Untitled Project.si4project/Untitled Project.siproj b/lab/Untitled Project.si4project/Untitled Project.siproj deleted file mode 100644 index 0e7a9afeb498a69ca4ed502304fc689e793bb007..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44792 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 diff --git a/lab/Untitled Project.si4project/Untitled Project.siwork b/lab/Untitled Project.si4project/Untitled Project.siwork deleted file mode 100644 index 7187890192dd0a363a7f591b2ffd5f6739d00ff1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63637 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 boFrQv4kkj8RpDfFES1cv+lZ>#L$Ut{o(>Ce diff --git a/lab/Untitled Project.si4project/cache/parse/boot_main.c.sisc b/lab/Untitled Project.si4project/cache/parse/boot_main.c.sisc deleted file mode 100644 index adb2cb0065e4e5616b01c5448861d163ab0da4d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7018 zcmeI0Pi)jh6vuzG1(p`13MwkvwG|Z!(ug2PAw)%MBv|>A_&2n$Te`8d8(M^Ts+>4@ z;NXFShbD%ks8KH_CjJQtnDnRz{6jQWqXz#02^SB>pWn~SyxrNQwv>P|#+ST(GjC?z zynpj%e%m@DNwy|Q-C|)?WvB&mtH0(ipygkG&!Nhyyd%LyAHqB=Lrm)J< zum^2|p`-h^kfttNZX7w8EgK%(y5-TQH*8Udz-EO&mY*P)_IQFZ)*wyG+ah?zhK&!m zKeQQriFtFF2famzFK9D1C-g;b3fDMydAXT=U4_A1Vc>Z@!BOlBtA|7p%t7cEdYI-?p7!W>%?;&XS?K(s za-Oizkv0f6w`El`M(+1@KKqmIBIuO^uHH0%_!{$zE8 z`563!5ZQ6Qs_Vdb_a-3>fgkIUre>A*X59<@g&aaL8UgqNiWhAoc-drH$$2BnDX~g0 zu22@oUL006UbV`55leEy4tX(o|E2~u=VU=y2gQpb<9d;C1LjY~i?IjISf2!C2grl| zgoI5d^5K8AoQz{@$%Q3~knqp6f2u+q>7pUGh?lc&0Vv*)4FQ+zN z?AfIC#7J*sS3jZnlms;1#4?%b0qN5U&*@hyk`+QEw+S%?OO2;nzGV2wsb-e}8g`JT zy0HTwv9Y^Q+D;00vkO_}Tu~b1HvvZXB2~?f-jtug{joypNBGw_sV5yH6>aM4lR9bp*$ar_-g@ zkrBz;CWHQj3V}D(w>o04BcqI4`;zMj&nxN()ar=wvQf659;?20U0k)wJH4jZwK+6d z&N~*`A|yt}%SIv({#Y#%k9r`E(|7jo?Hk;p7UvxacLyqKOH$!wBdP5dC`zn7AazE6 zclSWviDf$q6#<1(AW+tL8lT{_SBcH{<+c~Qp4D;&o(ZCMisGnQ5idSiBYJwXr9qm` zwWiDEx%+wwz4>ghtIf-2Zk)HB%F)B&EIp+$&Qj}dP4yig_?HbYW%hTLi(aXMLcXFK zQL=P`0>)F+%2h-B`0QNpEwi`KH@N(s?Ss_@Co*n-c-@%6$r)A9$Y6{G2*f{Ouq8Vb zuw=h11`o;LC)@m$VFq_#@bPklIUhv%pE8&;XXjDPQu~q&MnIg>?^D zLm!t7#!ybL8EoZVtO|&a76Xyj7%Gd?ro0CI5BLQ*6B}E8N}y-PF1Xv&;k<$j7+{d$0kuK%*RWpDV~fs{6#rH*RWDV)>h;zA z%O!WJa;BokWOFAQpOS9m3ONK=K2f2I{2la`FEloXFO+yrqUy2W4L@fQ0tD@zxZf3_ py*5z2VBKdTUB1Hscvg*Havc9-Unu#A$Lko#dE(aERmNwr!C$+p1110f diff --git a/lab/Untitled Project.si4project/cache/parse/fs_bc.c.sisc b/lab/Untitled Project.si4project/cache/parse/fs_bc.c.sisc deleted file mode 100644 index 03549f820d44a8943acf8d8315492bcab503e5ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7300 zcmeI0OKenS6vr>+Ra-`@6obLXKoE;z(I^3;(KIMZLZVfQR#{A^otBO*?bug_xTzb% zvL-HAh|~qKs3Zg-Vju()jF`A^gQ-yxOiUy$42drm#_|8V-#s(mT*q;WLZa~`XU=!e zckcPl``$aPi-Mpd2o|gr26aI&wLA#UAFm7Xb5Zc%SLc_&J9zd`0y;nVAO0&5AosTk zo8{1J!qIT+ds7AJEqjf_*v2W+z1U!R61k)FsESK!EotcdMsQ;7%vMxhU8 zAG|`Lw2MtQW`}wP$1}ajRehs%{i}9}g=rQN!h_1i!i7QuJ3fDQTch!yg+jgLoh0h( zGuhFBbS5+MoGxHkAaB+hC5VQ`v%8H z`%~S6!#x8e5_73HDD{q>jlx~RI9!qL@{mxq_k?2TuJ7sZ?HNdQ_o$J=f_@|13WB#p zQB0*Ahc zC%E4#xNCDh5gd1odRYAV5}p=s@6q+4U9(ZXLMOE!Z)&lMfQ$j>*mX1yLSp+qB^dxo z#a6S;D0{YEUn*nRikBNMZf#QT>;)6U*$fH67>e|kh-%V>2N^Hvawe{()O+zNa>-y1 zhZcwRGc=5$ejtx1Xl=(_AnE3e@UutqiTt;_!pdP3U8iaxsesI_-$Hu2o#BDvN%IO{+9(+0E9nEBVMV48InVANkXdKVkoplh)&}vkbaD`Q-VSv zmMvacJG2iwDI{yqO+u8ihF4uFrZ~Vjd$2yCHCbKhQAPrKjThI;o>5vDD7wE~A!Xm7P*`Eh@SYXfzGTbDL!)~}Q@Ql6-c))}uaqsV>(X^~ zb)1D?7KsP6wzG`qpVfJ^rn9n{-qc8MI@1!pecZcQXW^&cFHP!=wjW);@FA2DCiO(_ zyyS+1y?YZ~Y`j)a6TS(s&C1oNSVf-VUS&Aol>>6ru%lxno$DJ(@9u@^yWA%Cx}uj5 z_HcPhVYu>L$z@U>7MLh5|Je&gsca-D;2yx_D~%N=QEt-_J=3P58O=U6b;-`z3niNf zKV@NQurtIegR8ZP6#H??$)@S(*?UGxr*-&6@!G%76h|p>qM_*n;)H#bd`8ZIb83wf zWAklcht3acS8E%g_Kjy$TgC4_l}a<$2EI*ym`sdNlwv%T=P>kEA;C0W((Cum_v0g! zFgU=u1vc)u*e)cpP-|%8DH_EgqU|zpA9jv~9)H|%oP9_`i7>_*FRqt8gUaKMygV;N zG9@G&Xf>Wcf@byJ%DzFRap(V?&_X!Yu30ltVFX>KrlLnS`KI<;)sL5=_Y=O+?JR7( zRwIZs`{}l{i(M>%>LVyt0@M{Z`%QIGj!j0+vWGyMc;~)$jgGT8iNogXKi%l)_H8<& z^OHZgXTZChtthl>__Tjq`kuGpV~sAcVx#XH&e;c3v9a0b9BcGZt;d9ZBkHmU@%N)X z?Qo`11Z_R%=BC#$CM)DK1iM8G9N^%>(eYgG$ZD;~DhL;C2O`QEb+1}0|L zm1s4YYJ2GVt9^Ig>PpAf#Ea{XU)Q8$4nno+uT{>Do~yjw_iv#!`$r8^-+SjQ&ANb+R@{o7c7&dFHo&Y&m)d$-um>x~|d(G2W&B>Xj} g{N}QEwH{B7_Y0sa{B>WK<|F^Q8rU<2*E56P0l=#cOaK4? diff --git a/lab/Untitled Project.si4project/cache/parse/fs_fs.c.sisc b/lab/Untitled Project.si4project/cache/parse/fs_fs.c.sisc deleted file mode 100644 index 74ab51761d38b2d05d5df7a84425566e03cc6ad8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32054 zcmeI5dvxVhb;s{Ka2_E;1_A~_y&y8fAQBkV05c&xopF!}5Fp4S$xIRw8Is#1BUh_! zs;H=4m@c&)U9^oYZ=|L^N5N&*s*kDK)wEqYSw2=%Ejo%qe4w!^x8Kk2oc;Tq`@1*K z;m#jj-D{of^V`33zUSBGOn99izXDW4?60>Q_d0%Ky1s0DrzL zlliE4y?9QOJ6`2_0o-*|CUdOj^^AP^GrFGA^#!VbYE>q)Q{{uI|3rPBQ{Ow(?%y=T zZGM=46~-pD->qvkDW;l4#=UjLcVqn!7YZlDchTvD-oD*?_I7qxPdc6J5+!z>6ooO6{47oA|2k!M5JM-l3ay&5-w~V3>bY0hbkhzlsr_1l8?mtYYPj3Wa0T z@1@ZtlRl=qcaOJq+npVIZpX=StH+}jZfAr4LX{`H3`Sq5^&O2>HWt@~!aCh!d0d-y z5&msmL(b;7%F!wZRha(IRe(Q9Wm3h+;3waip8i^G62hE=U)$L`(6*<)|MtB(W=`;Y zRt4x9;R21%|60Y!teii!bmm88TrgnxbwE3JK?i2e{yk2Z40ED~XQ*1n@nqG8)6?*4 zwT0YL0Kazd_TF6E;GT}bp6hkVN}r;>z+LU3&r$tU!&BYtyzk|WRpBvFD6ElDH>rg) z${H53E!Q#B!}h`XZ}cd)dt}z<4FR&*Wb&fMsw7CUnpIlO#0#sU)!aSM)iKn?tkCp;kt5 zZUcU8H1(l@y?wj2m~M9ta!#C}@@)_JeLo1+{Z5%d_P?gFitTJvC{%0Dxm9M6>S&Nb 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 AyZ`_I diff --git a/lab/Untitled Project.si4project/cache/parse/fs_fs.h.sisc b/lab/Untitled Project.si4project/cache/parse/fs_fs.h.sisc deleted file mode 100644 index 6d7d5330e2723577a12faef6a50c291e0b9c7a01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20216 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 diff --git a/lab/Untitled Project.si4project/cache/parse/fs_fsformat.c.sisc b/lab/Untitled Project.si4project/cache/parse/fs_fsformat.c.sisc deleted file mode 100644 index 65fd0894c76e58ab44f3ed46e1fac193d607ed4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19067 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 diff --git a/lab/Untitled Project.si4project/cache/parse/fs_ide.c.sisc b/lab/Untitled Project.si4project/cache/parse/fs_ide.c.sisc deleted file mode 100644 index 3be2f5c8f2c49b8f79cb7995adc5bc8ec10f121c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7308 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�*;4-7tWg?~e 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 Trt8y3M+q-IBqo9J46^?VFfU^T diff --git a/lab/Untitled Project.si4project/cache/parse/inc_assert.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_assert.h.sisc deleted file mode 100644 index e476f770a09e6ea5cdfbc7fd83d37c2b540a994a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4137 zcmeH~J#1T56vuB}*GU~hO`8z$=_lI3S=tN?9V!%5AweZdXv%_wEE6ZHCFEL(+eS>0 z+97O_U?@_D3KYI3I#xtxzj5w-{ml=hls$JKEb=lKV2tsn!`v{DrjtE21}0nWg^w;Z7n4&OoLK2LTNeu# zUOT55W=FzWw*1ZE7d!Vc5C5X4cThV;AKb&W1uk+(;%#MTIOsKU&#R0EG#brDC1$E_++## z1Kw3KK>o-bVf7@0rb12Ke_)53Y zZ7tL@^rjFFiY;drCQhP5=3A~`ewtxQD>7p5RBNf-TwF@>txa;M*O6?g<4%CUM>ot4 zI-_mvvDj|5mJ&R2pLsA+vATbt1JV!{mWI zFtpQk@}Aaw=T|yM`(igfYhkLVd)4)_;im?#jJ_AxFJ8XbY641a0Hp(v&x*|ux+H51 zjTH1m{s8Ad7cMlG0mTAfM}w?M8YsHN?W5Z~eP7uHD4XN*IF;x6*I9W;Q&T)6-RLJH z{xaaIi93<x!IVF_V;S}P(%frj&%1O-e+yW+r%DZcKPVLsS0d- i{)+!x#xLgRnRv}Tty#U5t+5YJ`T=5(^_a!U$K}7hm#7i| diff --git a/lab/Untitled Project.si4project/cache/parse/inc_env.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_env.h.sisc deleted file mode 100644 index 81a6f4621fdb7ea1385e15619f35fc2fe217814c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5281 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 EU)Ib_#{d8T diff --git a/lab/Untitled Project.si4project/cache/parse/inc_fs.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_fs.h.sisc deleted file mode 100644 index b77a438eef8abd03975ced228bdacbe4cbb3db9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12340 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 RK)0pB^|{@*@A8s8>p!otk@Wxo diff --git a/lab/Untitled Project.si4project/cache/parse/inc_lib.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_lib.h.sisc deleted file mode 100644 index 0c041b5d1a9b1cb8e50efdb248b57f4dd0e502c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42455 zcmeI54RB@UdEXDazzUfU0a{qV9|tfP@Ygk9jBSjCm$ktLR#}8uaDpUO+FgZR=~~ia z)~RFJZfs)wA^b_&BzxVM@L_P3WHK;K0&1Jg(3$O2)0uQ=r!!UCNsIfbX_G=Rw)+2_ z^ZxI7?>U#N7369rnLabGp7%WGoacT2&)55&bFZY@iAge)B*$N>mTXFr^L{5u{`?O& zW$j;y(;oh}e||EuhyV1kQ{bDE|4)BgEP(INQR^3=@2LHz+RbW%YR9WV>6c&Z9r&~7 zdg*`nyd-(w#Yr+E*-O9t&odcvXf@E{J&m3d!f|#&KNMOS-4gh5;*8^1^1bkINejD5 zar*JTBx#(UB>!-$=66PtT&ubKMDp)!Ns=R{CCLvZ`{9{M(sNdl{Ofa*Oux?HK)8hZ;lq9)R^3RFCQSG>sH9xgKQ9E1bACw)I zh4%rqf1NbV9lr6F?ZY?S_>tio z?v@0vYEl_xJZPuSC^F)!((0<92SWSSiOD?&4vmec*QoRE*~9hmnN9nzWfaaj&@vuI zq5t|unVB7%n9?YFq={&ku`|ke7=`}Yql8e=waoUc7AzWNbb4PHhQyH1GRk-uhW_gv zW_n_hVXzr~wf8a}hN0hX7(8j}?3wZD>8a@ehKRC{Gag2v-)2Dclc6zj~L56jLu(g3R9tNS`ZV+5)T3z+@@~kJio^yNQzchNC ztrE-0T#*w>PdIdNbYk*$^$5i`so_O94Xu{FSN)h;wH7l+Ywz6?lcUpzCr1yC59ttp zyjksnz%La?wPj`1I@P4r)gnewy=8X)#EeoH>oV21s9^y3+ttu(Sy`{^i7t^LO4gOm z8LW$EQ?-i&f2TM~Q#E8|rJOTWtXm)5H$KBux2jzjE`z&_(YMdS>15$A8ol_~RGsR5 z6SE^aOEG0i5d1ndrbFLKJ-wBcMz4cHr-eUDUAo!wb>__Y>~MYG-qAw`W{3BWPL3TI zpT14!h*bOq!Lcnm>7S_P#YNqu{y!SMMeZ?1k^1$ZlCH`L#V4r>4)3WSnw>#2g&D6waMvaJIGrh*2LDr|x5#Ct+)nep z@!36h=oT`Jc|vfGnKON$#+qyYy{wtKnG7bVcnHhFh`KI3mxALYo( z)1Lp&joxA@W!gn-qx>|KOY`v9_{{9|)M3nmzM2{Y2R8cY+u-VEwDRxE`rDZ0@Wl9m zvE90!67e22G8LYxp_xAOip?^pIK=g))zxg{l^rtkH)J!vZ))$<^gY8mrbb+zoy|Gc zKsPd5#bHwaca2`+)l^w*Y!zaCW_EP;(9GSsmXN+8+ylQxjUnilhv-v1Z5@Kl&WY)7 zjfY^(+&irzxKC$NwJX&i`1{oug?@RI+J7t?iAc+dQO=D=anc^Dvk~}|PM@#lG6e?+ zL(%t8sDWy)lmVTZpU_XPIhCO(W5ZHDm1V&N5?>WAdY?Eh!3D^yo`8auUTpLdc&63W zHd&CVKR8-v?iZ?E9j=B`XP7&EE4ROw{#T=y*fXuJzS`UmO-lVQ>AJV7VR;CCgBt4T zTRj}Aztrd@{7kE>MODT$eVAsVzGrxL`tY#Uq_I0Byh!Z>;SM+o7=XS9;2283add6t zFu?Tqo_lxe1`7L!)JU`)YA7NxuT{&*s*|Mg<+5@TEniHTeVk~w>G*AGSObDzr-nBA zE)TX$@`ag zI0#UT%!)aN2EJBST#7>!@12+&qiRwuFH^gjJ4G4Sv`|JL<*a=%wa)sa<|&Y!G&rK)_3YG+9CMqYG|cj3Fglq?L9|wQ$_2Z15-2O7~oxMw}#8$9HW)K z=j!<_$^e`*RSdwEg6U9%hQd{FY8^MyuP9aCZIj$ond)bzCuXy$)^rIm48fhv^lboF zqq+N!_5#IJnd;2=_&tOc%OV_t_p32g`pz_UGg^G;Xm3&b@l`3bGOJRXF~a3)x92xz z9HXEaS=k7Uo@YxNLdMPs>z{ySCkNHLLXGMJ!6~_DrC&Be^7Cg(mBF6OlYH~op*l0Y zQjHvj;Lct8Wt}5ip6wj3ds)kgES!F)8U)9E%#?mvYqI&--n<&>q^Rnyp!6!8Wa)ss zgrJmuS?S`5&-R|rg@Gzcr|RRA_?dO{u5b~Yn@~!>taR?D&y+H&6a#x_rw>i;8J!&` zgRWL%;{?IIjihhQj07#d@=PyrVOm`+${Kzw{|h z!i8|+0zLGd2Tt4FPao-NUCT;&FjF7>%p{g&U$|clf-`%x(zn)C^`ZOzs65+}Wizb@ zs3RXzyN7#3aF+x6E(bQkL(d%PVH04gEWA7f?b$y%jdib8I}omfyL!{Nb;If8zQrRw zn|&Z&u`Z85(u*BFtadP53qM1R3DUP-Pxxtht#~(p-jW?AX7)@?&R~ZDwMjJyZngBS zmUGZce^7QB4#)`@J>(gRsS4MreL6pv@onleKV(+y`EL8;BRy{p*TO2HTc4#M|#fZ!a$WxFiK?z7RWJXyj|^3xEOwx8Z)F{4vC(FM|v)h+*FzI zXGSMxvG4V2_l2u6t}P2n>0`aZG5JN;|ams~(DuKp~Z{J1j{g~RXgv&A>)Y4yDZK?L| z9KLS*_M3NJzk`gsLG5!vbH;;a`t3BgT4GDMGhZpU>>S>{YsYm%=)FefAbU-qJLuVO z7sI7r3LNFg+70rCa1E?t6FmY=>`l3iB1m0;2EU>YSHJ)JSzX+v<1w}L{ug^|-#OC5 zwM%++begz7AmzhV9cIVym^MWR+zU3y_r@RY*G;T( z2->j*W@mTCS|kGVJf3Y?8Eoy>1uW#9!7dBz$oGm0vsE`!MV)u*w!}KekOP3ejYA&? zm^f}n276&RY0jbh>&`bO6i4xbfc5>o+_O*W+-A*kPTs!c(Px@Ph8@%uYHNYTq9(@i zv4O&UKIRx1GT@8z!{)~`99p(+z(cy3$)F$&d_Wv;Se8b$VE(_AYk^nG9MG2V`40ET zh8&S0lfU1ZB8!sEe>G2$_%kHt)2(t48&SBL7}ag|*Oekv>2lQ66d|<$_Ax2`ncA{s z`|mB5CKkqbt8G-&T&l4k^SHmRsG%lMQ~`}L9&q72+TeA|(kN>B{-PW;4*oo9Hs5_{ zZ#iP-6&)+3h#8a-(+Bg2;S)jT0BuCX7?(NYy1%Z7!GOSz(<4z)UH)F49$0Y`))3f% z$BH_`v*icW){2;-h6m&79%!vP^MtM`*P3@^cNU+fa%kDQqJ}hK3yh~wK-9p`R|BhM zX%sap`T$j@npD{W>+?r_MTua@YAF~Gj8Aee7ul90 z^2K0j(W@&!m$c{siBFN?+=sSqmoJ;kTthZZxb9#qgKY@xNXWd~rI_tZU9!cm6<}?$ zZh|FVm|N^voC#Uh8SIJq#rnxSPn?pTUP{pi)w+q-zGcOS%;SD% zyleZmPrj{%IQGUoWnKAJft9C$kEwMNtG8G+z=_P`erK$%x_KNQ&)Pw-#07a!zy?Zz zoIao;{p|wmn{>~I)w&6mpki+CQ6nVH)ou1WgKZzkxI4z%_f-XLa4t^?zp6p!arjC9 zqWK`g4(vl}#!E43_fS!oHxvq82d!14zzRL8=77FDlN8^h;n1?qxT^t;PUg)0ZwdSY zaqMeZ8ZG3t%FB+DP89{#5Tx9HN#GP4>qM4DI_p0vZw|_9NcPd)=Fpho3v8 zSoc!hbB)?YCZEt{WX^&6;{EyA{AaudR~|N1aJ=qu|J#C|%fzv;WoeWsOZp0@PBp2b zGg|k!pF#+~TFp9t6m9P;PH=k6f`7-ts#E2PIq3vlBtcw@o)P0c|dG zO`&N*$zJ!mH>hx2EbX!9g*VY2+juu30lmitr`-#OB3TE##U_yXW_xTzv2&MHZ5Bgy z9hB$!rN;$-E+O#TDiwp3rvi|pwO}IIazogRr7-XUTzkd$9)-~KRX$I z9Bcle>pV5M-)35tM!B+5$rYo?aczzkKknx}I-HFO31nFs=^Xs4a{f7O!s1uohtDw` zx3h(X<8!M-mPRV)e_G_8PRZ2Vom1xX`uOxgZjODvzzXvZ@|Ph_=wIAodp+#$e^ZAD!X-oYcK&n1Mn_VofU#>xyP-YvXNXs|h6;E-jV@s^z1 z?x8smu({yeAt@eHYkW;fu7~EVddaz{q2EpC#!2i*hC_g*-x+Kv;k1adT*ZOBtV%uq zgWR!O1n2>^zOT!%w|u+#rt1RT!#9jvywDmK3ms-ugi`*65RblA@#53MPmy7w-xtT` zPpBFHRh$r+$9>`N&rXIP@w_SvzVM?PzoCn_mGD%J;Vsi~j}9|hlCRx|SDyT4ruXoL z^;>lgyy&2S!SRpvB1N4XNhyGWoe{#{{IwpN0(Hm zJ2o@x>9W?KgBlKhtD5y9OC!At|F>)g0x2h8pNQVP_HqJsmj!MsSe8a=`=qwj3V_WC zwa2%sy$wS4w_9uPykrf1cL&$<3omd~F_~`FB7DQ-9X93?HDy=+HPqfdm7axAj&76g zF|`%poeM7!z>Bf)XE*V>_JY$P4u>r3jJM?6wzXGa=fXncg>^w-lklhtSgYwKSUZ=f zb>hJx%Q}NCsv*wVq8c$VR(|SI!EEO)$OetDu!0xI5@_ATYv+Q^1py9O))}w8 z5;g(toSO=4-*4rai-C#cMLD-mA2T>}0hv`4&UNWqcV1Wk<}w9`Eb9!G8RkT<-RrO= z;aUn<*_FSdwkXH$o-JO7T`1W{)x5A`gLnbcVMc{%bK7|IwH8)v3_Yr5%skyf=5b$` z`?HhbM?9ByizzypGrHJy!U-CJ)v`1y_x1l>UPX)~;EJ9Ixc}myhbm*8$kIq>-(Qw> zGNYW(zNx;CTUo?y##C-tEG-~4>W;%uztm_YsOVN0;vU1Y`Mi0z`Q(z=c{BUAh3Ms9^U7=hdavB1-2h9Jhu2|Xv=J9^;IIPbafAKE?`I3L4VE3!1wn#><%^<6g-U(P`h>Km|9)3rI0=RjxsaLkcxM^S^(D0hC`Nh z&Yol2kt_?_KqFtI?~rcr&LIom(giPA@g>IZ7G5fq9qA~9L*S*~8E+4lX2g7Z4(ajl zip#58uz7(?N?WiM$>zhm8~_6rCwCLD%Ol{NAaKaC&VZR^PML$+ZNU=N{d(~3kgmnM zb@tc4k*@`S1tga5CfK+fZr4}}2?HtmPkFXRE=f2>8yjG%9~1b4NRu z|LHyV>LH%nV>8YX9^n;{w-58*P#A@f;cqBldhUaFs~|W(49#3aHsN*-nKt=Rj+rF^ zTL{meyo2H!rVEnwJyLwbls(IUS~p{cm1al{qq0g;yUo>Y_B+Q+`-<;v`+)TD0VDim zHO8WkOu1i_PaVfp*@{e$&r&Pmpc#f;g^dKMc$@+*xcHVj1k8? z$IxrthTWzy@gG5pFChof4f$d7?`?2sS!bKJ2_o;qP;gdI1Tc-ADMgk>2A%uT8j0At z4?``sg1Q(DL3~fT1Y322ewIWL+t#jWhZ<@%NoX{HW!%%sR z8hk*_Dv_m;%JkRE6D=9F-D|%=Mbt>?sjVT|awtRmMHFU619!K|(2#T+n4JxW08U>v z#Q#l-qq_12`BmNw^$A-VD$yO%#7aFP*?p%!neU5*7yD4?{aXvb#0kdlri0@WK{5am4!PeMa4DMG?ZXxYtgo~W8&Q3H z^xOihv$vaIy)OZnQx^_d))_25%_)mU?Zbc{uLaA(S_^BzxV(3^QglVK-GArd<|Fwf zy5~GKuLWdroHaVk$Ts1CaXdQLnz9Q^KUhy827F3q1CzHPNm=R?p;eLXV2~?P6Gx+u3=;x2t)^$kIsVfS&dXC3vX-fXZHqcRpKG#DUQFO+8A7 zp!cJ6BH8XULeT4PPgyln>Q+gMoo^?>A>h-O^Ivxf9~8EwfVa7GR+3+Gc7Yd@(YlG( z70nT1u3?*z)EO_c%PE^{yNXs*MvsIn$G~H1sSNN?UjY_1IJlc&UC{vPc!ookbp{K> zoHFOMd9qxR6PJD`e@F79F69&Kl#2Fv@sx0>5FKVzjF#KR$F64W?(x0x zQ^LoC(40f}g}2}Lz+;`2N*4Ab1P#7I#|44&+ieiRFf(%*ZAY&<%&=v zLw>jQv)lp6?%S3J4SzI_NpK&$+ZDmU%t7OR=b+ibr5WMCEjuRf9cqKOuE1IRVJqH- zWH+4kWOFHeyYBB%bKsyfb{NMH_Ap_e^#sn4uIHXxf&z06-G41XQ}%D7q-caham8O2 z-se>DxpqmvNW~+K#@Z0Pyo83#@C^I(km@DAmB71I5@PciDjYIYnXkhpp=v(DBo3E( z;YuIQ!{w8*;c)`ABH8kxVrTp=-9vM@fVo{cc&$5Q-i(r(-r8bw+{sG~$UN?M4x3Ub zoXLNm)S-jE(}UhK#XT0XGzy)OUn{RC_Ie(U3LRX8kx|LR1KgiKTb4#D8~R8|mo%yJ zB1^wt&waQF&SNgz8EaV@=^Xe=RWk+19VCXwT4UQ7b zY*`wWhUH3Wc-0EV?H2?!7l~UXvNTdT@L;*xIB~)n9aS)NvT(q!RI^TGX{2+xqO;`r z_7!YE)oMNjIztfXgOWWI9&Q(-kA=D!=w7FpgS`TWEED{7t<#t)j`Est4Tf!EBeKNqKUYZ0JHJjAB3Dy-1V2)!r zWLamhr8v$j7`S8K4hrr{fctZu`<$G=qzd-ZS;eo;)g-@MZLJDcSU04j?tzYV%l?S2 zbvzSika^q}?EdUz_|aOu9Eo8L(Y763r>MbCQzOnTOQVvoSowekXXk`=?Y;00U2f;Y zXShFnwk(ZQj(oLTxk}!d+Phc(6r!AYsRFN7sxl*Z=e&HM#Gm~3+?YnEjmOs%fy>3ux8emyqj2eNxvh*u7yK(ZD*{-vk3D%L9VTS z?PgBM=1MhnUKZH@#^Tk$dvrJ6b>6Al_Swe~zb~ySvyXQD7)E1`DV-v2~bHVcfj_9xK>ad(dN-3Yo|K&cL^1_wj={ zY$T#1QU-T;;^thdc9M0bouu5cUq;lkE&y-Z!NcmJZ zN82QH@d*u_<;6OYrIF6`xbieh1+4uxsUa^fgfJhHN|HKJG;Hh73z+{20K*bU-7a9j zv{&GeW%Bmx+9oXt*K&w*3I$jfB)dPtYDaVvt5X_W4i^KntTR?-mlN}C`=GZdU`cpS zg;KySYqfe>uX0IU9E%$I-304x5_51I!y(H$gDu5z`)$(7xAa^a;F2SAYV(o}oLp>^ zuF#M`bp<1)+f^{GF=1cF#Icm?$8|k<{~Kj; z{4>?prnPVD8+qX+3-%dPOC@_=%PE-nw;*sDv*~u7vAxVSWYdQ7i_;NtON_Mh3zmwv z>~o5{TIm(>f>LkyIR#cQa^FT_wP)dwy}e$n%(n%ra4m=5{_|hPmT5ly;dI90xwP}IUk!8rsX}Mot*;wVl}HomPRTYN)L&eRDB1eKWNqF&#x{i6z5B!SeJWl zcvBvV{O24`ZX-g`<}wG6`*O?cO1lMNt%ncuyu2?7>q}vI_kRiMc8T%w4ler^4q4V2 zYmo@}jT63fykNs?2_c~>%IeROvJkZ#79C}1A~=MAuxmiN^I zrh%d&Bcz_+$PGv~cv`XKT_v#Oyi*S^$KkHSjEVrwqW~j8 zTx-fD)t{st(d^XwrErNkhwck>e>j~nbo;p7lIPUpeo_za)Uzy&Qg7~rV@N%e0uTil z2Y#U%#<47ols1$ioHJFvvU6~1tn!te5ryL5kMa+=yb3HT@3w_Ea{L1}!f>NP(bh5t zjQi^f#ddi&AYdy`=3ohhF||I)o(nroz+&-k7nwK&?O8Zv&vpjuzz=DlryV5mPIFMW zMgrDXbndePwJg~SXBKJ4YR5fJJDePcx(+jXt!amip?IfxS1xM)vpx$_&pJi#3A*ud7pH4NY&ATZl9k;W+f~RU$iA={mVi^srmQ@zU!X?M^ z{LRg=Z|0SBOOLh2VJhET9lkEkKPbc(5K6lJbZ1+cGp_sc$?Mu?)`V+5#Gzkq0gc$|(!Z&(xN+*lqiIvCq6(viGPt z>zGv>={n2^tC(yZj=9$4^JP9Hfve1LIBCwI`$F6wPG=0gK5mzBUg)@=XJa^Dx&VV^ zX_Ou>{mGGLs^}8Uy_6p8G0W+(@*S;7KW<%<8ZzP~Z_b~>_%E9ng!|y#N)N!szzk60 zCfLp)Qv%y=O|I}H?r0ea z0&@=CUvIQ9pEx^KgljR>0%ym9WY2xE$c_)_o=er%!fI@~v{^gouZ4Api$un|CqHcd z+f)uMTQ6Q`2lulc!Ov2|7nY?_c08~D>#9>ts$|FL*jVLXQ6=Bi!X5&~JGOZRQO{fQ z?BM^pIuJG@I}FSmH14k}JNPw(&AK^`m<0iw3#H%$nUTL@Hz(QCKQ6wS|3TgJUbVF% zrbx!HYtlW?S`o95UzWWCBzo}Xz92K_(EZL)vnAY_&%u943cWZB9p?sqzPQIimPV2D z^i$=?De0|GPwkr?JxD}iQ}osX*Zi7#k)@H|-Ky7&X{tu#^uEe}y<3z+2A*iG*NaN) zr^8z_{v$BpXiTQt^*S~phJ!2+Fq8KO4+k(`_1Y-4R&^+b3YP(&{%T1I&f=1MEhoU+h_@pJnR?i?1n&vRVzG4@m~#wbIW<(WT8oOnDDR$sww!ldnG;Hn+f$C00hw#1w7nid7UG3} z0M85{c(>N3LFtZB86D%g-#K0i2h!u$cgk4n!c`CJ@Q|(#%NcK@LH*MF~->y7mQ2O{&h-#Jt~S{~Z%{uyuJg#+oeOO1KqS_m5%+(v3N6hbRQMQRL4ma5&X z*6lJdsWBk)xWC>|!O`)mQU9KN7*gX2o#T(@EK7F(6UCkJVfSkf4woo*XqEGuC0Oly`^Po6f^rjU5=U3*Xr_^!S!+K)xNLr2(Nz$ zQ_9SeLZ&{Phs^y`8yhl)X0GWpP4Id{W>MkM7alwOGH*_@r7smBvt8qTLd_w=_2k~z)iYa{^rslg0W%_l)k2No zJ7xsTq5`IIYaTE?2RCMWO0dSbj8Ebw*3JP_s;S}6PP|q3_lH(kBOi z-mn~en7ryhn|yhPYyQTb`rC}{H9BQe7AwRORCowglL!3)R7oMWCi({`4h{?s zCOFhY1}D)(qnm?+anS|i;N<3@aqD>fzWeTJUrS?+aWa0%mvejWdEYtr-g9q{ZnG@g zviyfY%V$~30n6HW?$i7TORjz1Xr*uM&Ep{LR_j0hl^x*O_kdkk&^_=07zFv-4-&E4 z#lVkHp^4+~a1Nknmf*XQe5if&6UyNARX3JD-5HCmrO{^W7FgzyUXpLU-Rkm}r>X!Yi}1OXl-^fa_J;vCl(FCP)Nr!8j3 zV_}EKeKtH6or0?oYzGN#jXcM-$__^d!-GuG4Dw17+L{y`|6z*Bq~N$#3i<>}NpbG!cDbt< zi9{!+N=bMLxRE-pg*NZ~p^S0&6mqMU2i8P;^Bp92yme1rfi`i5n)sW7n zGPAq@JOkcjVtu(7uvVYxr=lH|_8@fSPBAciV6}-EK71<#xSfn=HGH+H=6wP2Q-j+r zU>?R1^+!gwP1 zJJX`HY(9#LuUZsSB`C#GTyTNS$@!dN zrsch5#p0`0mQFzkxKcTd+4`Wh}e*IQ4@b=Y9qv;bj zBK|slL))k8Wc`G!g$EJCMf_wx)pc?$oZ~@yV=TlJ;%*G4G#E?pDd77?>~oF&{wEE{ B_-FtC diff --git a/lab/Untitled Project.si4project/cache/parse/inc_stdarg.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_stdarg.h.sisc deleted file mode 100644 index 74e580ee572c849f72339c2e78c328483a82f96f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2442 zcmeH|J!@1!6ow~jOg6hlqb5Xca$PK9Bl!Wr4;1_;44McwvMwZ%g%FnPB8r8U`vU}< zfHnceCXIq!3jab>#6mk!K}!o^e4n{ww0Gp2GKj*fB9D!;N2e-hIOF(!m`jWl!RUZx&C>q)IKqy z$#CwjW@J(WkbD^{x#d!@VMlzF_OY*ohdmlmSP%>7(T+McOBRbxJvaZ3E5RB;l6}&3 zp{Df<;hMlB%N6ucW}~LYe3RtscrWKD#137Uny#L|c)B`0bLQ0Lv(OJZy!Pu#nZ1ekMrWF*sJzWq zZK*Xa4;mVXoT^64#b)ke2h71tjR0mbIm|9@`%}`|VCU zw>UeiP##zZR7C9P=-t7R{g^Kn??*Qt5Ib6*U!0wrPdZ;C?L+He^tPPj$F^0S=o(ZP zE9$*IpU_JFx~zU0D}9_yughaS`yn)BYYdDQDN%e|DbuEnJl6$#ek8RGVpOo9j_oaW ztC$d|AC5aEkeg%gje38FEzv$N`JCQoL{qy^Yg|GJq#TJ_UyzM5*b-qo@jkmMr0iX& zHCvq=p48{%YW(CFhmXYJL{G*cX^e-z!66rCkAQ|tcr{z1bzXjxL&A9-XlB?_VoQYW zq>IDW?ZzCz`8iw@U*C_XbI2@k&ECs)>rQ|8iZF-V<~)bmZaIh96r@f5$sF1Y@`Zpu zwDD)QM7F&Ag1;m31w)$Y0Z&XeS_BdzTZdnMqsfk#9D7+_H azdfvQnhhWN$omYb{(+~dkI`wp)8|i9@$_~8 diff --git a/lab/Untitled Project.si4project/cache/parse/inc_stdio.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_stdio.h.sisc deleted file mode 100644 index 217976c5afd258f41be75a4d96d22c90a7105e18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13099 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 diff --git a/lab/Untitled Project.si4project/cache/parse/inc_string.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_string.h.sisc deleted file mode 100644 index 1adf4a30f879562d9f7d2969e8c39463b90d8515..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19591 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 XpB%Gf%sG=fXX_J-`wr(o=ZgOepZH*h diff --git a/lab/Untitled Project.si4project/cache/parse/inc_syscall.h.sisc b/lab/Untitled Project.si4project/cache/parse/inc_syscall.h.sisc deleted file mode 100644 index 7ac2227d99e1ae48cebe4173840ca19f3b55c826..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2828 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 I2AlN%2KCU^Bme*a diff --git a/lab/Untitled Project.si4project/cache/parse/kern_console.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_console.c.sisc deleted file mode 100644 index 755a4ea3dd470e1a09ddf1f3be59e10c3fa8c94d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23244 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 diff --git a/lab/Untitled Project.si4project/cache/parse/kern_console.h.sisc b/lab/Untitled Project.si4project/cache/parse/kern_console.h.sisc deleted file mode 100644 index 66b4fc03002b0004316becc57fb1c09435796270..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3681 zcmeHJJ!}+56rLD!4tEfK&99JG4oFbN1qD)~C}17IV0_>+35pOW<6CnpoI7jpjFbu| zQY19M53Ui0~(Z6!}qBqY`IWR?xqp2X0Y z9Cw3BPv-mb7mjSpQ@@ISIk&=?0oYFb`9f^8ldLE6#j`w?$XF)uL2C)E_woZso};~_ zdnx^r4zU^lH4m~Aj{S~}Tfo1;7DN8M+?W!4JBeLCLEi&@3H?0Qp2T7Iz>lDxfQ@JC zxgyuJZ69I|FddZi$=brg9QPI~gQbyjX}DN9Sh<8TUBuBsB0V;B&VA&jGQZXiE!c@Y z6vlBQ4*JJxQ8U2bcyOpx4$g*gJ)8<^;dnKSKMiJ@VH`}yjWhB1x6PngYs^elgLqv$re>;P5ViVG1C5A`jwI51Mx7Z+dq$g*wY{nePOoBr2IZ61chh0BtM>UXB_z@VQ_likGQ|WQEDZ}VtH0LaI_ZIF`$xd# zp>K+8Hx^%?6iPl@43|0^M$-?~OmD~)g<3d z^1$|7p)(h$Mi%5VVQYd}=FW{^5~U(jPy0N#DUF;e-`UeuxZH~QuJHzM0+T2SNSCRn zfd@9{3VKg5jb!NEaxX;z&q_2tCD;|rv$yoFYN*7fjWYduB-3LpUwTK}yDplnSHsC5 z@pPVonO&xLOY1~v+Om#l=a}wmdpY;l`XlgQRt^_OM~cIvK;asF-1T}9nYP!Tpgy@1 zmWTCfG+D+@28W+ep{-sLjkYY*b_MCT`r}Y@f*NoetE1^ygJ0XH*Lr0qTc`@Y4BNrk zo&wiC-KkAdX4@CfK9cPv3as zNh*{2(!pwKov+JqPt&m;MG+re?=0*zY=8aOE-bGSuh+XMZ1P=VoFug^9qStu_74QM z_jYHnv#{;@z8x$BL4rI;M96J5!4?XRd z7jx)YKfNHu`rmxI7IifJ<;yk;UpC+D7hkmNd3!V_u9-uz?%Z-~GzNVYSvb9I8b58X E00q8PRsaA1 diff --git a/lab/Untitled Project.si4project/cache/parse/kern_cpu.h.sisc b/lab/Untitled Project.si4project/cache/parse/kern_cpu.h.sisc deleted file mode 100644 index c5a4b5a549a0fe7425657a0a5469152a1b1586d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6385 zcmeI0O>9(E6vrQBXkkjRwG;}99T6jyo+CSIJ_hZp8V zrIv%0A)P0R9?K;=8$T0nb>u4<^QAU_yTJ4MH15#&LkWFScv4nu7QQW^=e2vi=!@au ze(irt#(b|l|5d{JJ zy5qp(lUiX_ZaHu?)WW#E7H%Z~AP&y(Vd$bvQmDCO#4RtVXb?f0)M||IIG>Cj^ zm#+I6Eny%RYDPlipNV{RY*KaZk-T-;t0|in{*8w^bRENuT|n@3*{n` zDUthvgO2hlK{7m(%pB-lnY6p5{} zpL{v~!%%wjs^8OS_)OuZ8X02^Pd=o@pF^5Di1YnEDE#?u=O_6PsAe26gD&r-^YN-xQB|;Zc7FTQ2bPK#C-AHz*C9(06 zqETzmRMm*qtA$8#QzRQ*0&0n8yrf)hsYA<1RHsqRC)$$0(;NTnr$G|nN?zE@Z-F$n~2_oy(`O|purY+DrzgVg^`Kp9J z5&Al&^Y~g(!VkV4`AT+xH`CWWl8$qT>@*#L5gJaGH5b0Pk2c(~c&_i^r2W4pQV z<4}6xYf-!5OUuW+T?O9A1zY8m>j@Y1lR`^%XlgY_ z3KP|GQ&*TFK|TqRW>kl%l5^r+do`#Mp5>Md(3{m2?D1i)8O0wP&t4^VYvCtDMY2X` zmxdUv1|cMjAn$rGB?QjtlQq8SPuKYZPKPZ8)AoO7+dtb{d&$?eqr>@y-+=kguY-yj s#Ng@GE=};AHR2NAb47bjd=Tt08TQX)?w@mj<1>Fc7Xo;H>*8+*#H0l diff --git a/lab/Untitled Project.si4project/cache/parse/kern_env.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_env.c.sisc deleted file mode 100644 index 7ac8f794b8bdce0eaf24fd9c85226bc149044e84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21340 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 EKOZm=^8f$< diff --git a/lab/Untitled Project.si4project/cache/parse/kern_init.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_init.c.sisc deleted file mode 100644 index c6b6372747c606f89fd06176912a82094db5d7e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8350 zcmeI0e~esJ6~`abW!rVwvPz-F)Hp_sB~9$Y7O1u*n~0&w8mV+E6(if}n>TbayEEg= zfSPEs@h^=2fr-(IKiCo>DE!fAs02-^Q4~qgM!*nb41cQekN#<*$@BZU_nw(IZ>O_e zVEw~*k~8PL^UgiL?z!jP-RbctnvbHf8-!6IihjK_ie7!X;P8*4;A4M&^(thK{pyF4 z&`r_*;eVw9?EUq^ZA$1H!tV;N748y_2_2lOPrk5tZxMccOB7996-8$xd*-DDC-X{C zlmNiDS3U8{S-@77;8Vd9k zEnF1t)$_QX$HhM)<1dT?n|ji2>{)6y zYTaf#IMSZ6R}LHvj^Kwma-|LWYdDIVjZSkhkt4c*$ko^m91V`(*LHNNE^ryOmdY)8 zsmKyxPdFce1~15lc_Dp+G~I5j>!9s}b*smV&A2WE!4Lc}Kdx1RewuO; zJy6bVgMO061EFLDZKvPS-~@hcCnHPNZM%}ivP8(5HVzyOUf_p$u}x&br90YfOea2> zRKm^>^dxHpjs`dIYr7e_Gp$-&YkxNE3}M?uaWwdWALhrJU$2$U$eoE>_sdCDiUfT% zD+G=PC-B3Z3~s+Z1_9Xq>j$^L)2cN>qaD>-!hXAuFrn`h;sKsit)yb3*=J{p)-OD# zX@9TqwBd5s%SnVZUwk5a4wgHXfR$wC1~=2*VS#nSHk`)HGK< zttZtYIwa)8L-B%jlV#7G#O@swNK@N8KZ&<4wUS1+{e^{kr(2HeRD{gugj6IJVw#G6 zOy~iLhvnyIindQksK{7Id!Qoodif3^n+tqVh#C6sK4BhKemPU@yTR~g>0`miy?Lp% zuvD%!2>2S|mxXK>)aqffd_73eVAU43%8gpw>9&tZ149o6&F_m^z+VY6h}{m3|7ND> z!}BlGbUfNL=3P;V@dao3F0pjLX|EMMDT-OP|Ir|WnAuZ*pUasgn=MWgvsW&BuH0^5 zfiYvpU|Khu){8%*i6Sb*B}-6QM|yOSZ58}jvL6<|UpQbE6K4$B+D4gk9tdtcX;c(A!fY3;u;ZZ%-m5BoU3gxy*QSzQ7tnIJ zvo4^R!*BwDWNdwb?Mic~=pxNOtV$ zJBldt;UzeDC=>irt=UgZiUNWGOdskcgTJiH95C5 zd|?LKbXhGqdtvZQJ|x+`vwlu+EhRFLU^e=uXPshHE_>ahmh4YN$+JggRkHEPayy^w z^mlZvec2~FF9Qt43lG;PPd?XAmg3VK8<{K(NNgl&T@3|HHagi{1J)Yde6F*~YbCd_ zQ4O_7T9mF$0F%0lyrKZ~q?0uFpY%euvPX|S#JQ}a$39ASAu!mJ$&EGFJXdjgp zUk@l3IQe%xvQlO)9Q)H8lc)!)jAjQd#yDiqlF*DI({t~#vg0pn7|jk^jD1iDy-{dJ zk(Dw=KmU(8_idEeL7H0eR}b1BNMk|>wLxpLbq-p>$#&Cmb<^SB56;Bn>ZU`wrxc{i z-_%gn(IFc>6DgZ*G!!t|x{ppi*^*}B^ecliaaqak_-Q{`b^+YTWNjvrm^GkHdy|b$ zHrFe|W}TtW+Hjnxqt#D8=WgrdLZjDX5#zAFi60DDSVf* z>EKfVZTYhPz`a;MIxrrj@>YSld8_QL(@r7dKg*jLw)qW1#`a~|Sg(H8=$l!awex2A z|3(~_?Sp=%*8%H$22_ilzU3y@)272`E2}f58}46k^Ez`F5L7&Izx;U)#GbhSq?@nX mgA8(O;O!q>TlKs*$Zif4SvBxq3KU|0Xg!jVoen(b)Zjmap<^Kc diff --git a/lab/Untitled Project.si4project/cache/parse/kern_kclock.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_kclock.c.sisc deleted file mode 100644 index 78e9d707bab01d6ebf07159368a70d917bddf235..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2820 zcmeHIziU%b6uu^{#>CW?wl@BNh9FWENeiV06mje#RM9~lY)L{J(xwtybrKh)ZVnC| z1iKV0BDnY;I5@c0K@=)Dh(9}a5T4(6-~C=*H6)>kgLvTN+MouwzgS69M$LcvS0=!_-wW5t#frfC+C=VUV)Fm zc^pzPu(YvEXYGu-4k|(vj_rBffrd47mn!+``9d*$5{vB3+(=~V6!WoeJP}J)x*S~B z%i&C|@VvfY%N%_{j&iYD$(Hlev-vXeT!M~zJUvUSo3WWAJYb%Y7T#wPT66DtN&NEG z{5Z*({Lt`7ZYX!9TAV0cb~+SpsTaSB-sR3HbVIQZnS|Cn_dM29i{24LzfmpC6=@m< z%hKom_{*FRmZ#7~k!uYA+8Fp6XWH1COoGkQk3k6*h)rPOoVqpv$HO@O`2m8QG#x>8<0X&Ygg*!X!{SXDLfkfG8kg}sK^C)|>LDF7BR9}+R zX^kNs;9k_y5LpBRsNp4|#36^@ja#e~pP9w%w*Wb`r5|OZh7}WL*1pw-S76VENoT<< zK@UfgI;|U{r&fin`TA@rKn`DC3QV~l%tSg%hmbS6)*LzK;L8N`Kjh?5NX6t?Iu!Hc zZ!z^%_T#h{Cg$d6=pDYE)3}OJ_b(tkj^S*>;ZffW`mvfu*YhXvsA)+eWiY`l;qljo zZv&4phetL13};h!{RhZ~g{!~Su&$Sqq$}T2!v_(d$FUk=P!2-82WzsRyp91p?ph}> xS|z#Gv#+aTZq`LoeHNkk`lX)u??E<1QrIR9M6N&bZS;xXZ6olnCz zD!B9yK%6SUr4p3@AtWS@91sUiTuMPgqK8W5o7_Pfe!rP{>$SVBtMYL`Jo)XLeKYfW zAM@s|XLc%8S8D8juo_Wn-z`d=|9HgmSCHhh-<}_*?(AphCMZYM|MXwU0MGtT@LmM; zCio0^H<<5ZVA4B3PG|MfU0IGhsF(L{R_eK*rn9f?aXRWr#?#k_e#_qE-|Yk*C@o8S zyyQIXNG-`;O24E}e7-}e{jmK!v;oGy!?{2Fiia^i1O5o0HSi{k<-r#L{yO+x@E3T0 z7VBwOY+9M`=`xsui9SmDV+=iZ#J#&8tJE4x#p?0K(n7hiu+&(53tAXbaL|&K-U7#* zn{-pDPh(24o)$`B6tyE=sINCW8ow0{llp})YK0qmJzNRG=&0^?!brauwb!DR&5rJ@ zx4RpGZnd}cfoALEMmGp`bE{AT8W9;I$x81T$7!+Kh5lhoFEhm1S*kRu^@V3JWSQ97 z5b0s5OM~SR2pQ8$2Vp0VEi{flRh>W9sMY7I^%GDrZ#lo-N)OL*8a!cyjG2(3jeepB zUpi5I8gq0t~)RL7RoZ~cbDI0Du=BnU}rDYEw zx5-Km&hc;(4+xZug6=^w=B^ z#~wJ(*zegRrMyg(0?Zfozwp zgr|V&T7u>*#R z&tW|SCOrXWvXTvpdXng_T|GT7Xn*Xbb~COV0`LY>YOolh4)1a0+;@3+<*;AS0I?2v zIRhz})aeA~yO#zr2l_>@ven(>?&R@o<9MbX8mLDc&+;3t9@(#1XWU0`{^@#5W>9*W z=x(SU2Lo0JZgC*sJawmf0ZU!|TLH`QQj+AzZz$k{0NCqU0wE|9AOs<*X;eFuNNIX*cX@4E4`x9uK Bm}~$5 diff --git a/lab/Untitled Project.si4project/cache/parse/kern_kdebug.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_kdebug.c.sisc deleted file mode 100644 index aa08d6cd8dbcdf081b43e04883db39a3358dc610..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10643 zcmeI2U2I%O6~}MVq)9ii>(C@k_)xbYBzy>Bsf9vGBe!abf||&w9ZCr8dSkESCF{** zy$(d;!7nYPMbv`O0&VHO^r5N{ZlwqiQZW)#LPEjduOjV zc7kzKA&zu>=AN1RKj+MxIWu>Y-fN?1FpAc^M;xt=qWgD7(W~cIJN}g<{M@f!-H7hF zA3xuPye9fT{jbabYrkE*Spl6BKP~PS^Snk3O+S1wH=l2=r#-ediXLAVMZI6RnET#8 zUd*90ial)`PsZ|SeM&7y*L%525<8Yl_HOu&AL>L-c0Z(-e&&@W$&&(oM!L0m!;k$Sm$LS{zQMK`7(C%qXnpN+*_VtgEHr9FYOz0J&c zX}nTAdup=b17h_V1v%+0(OzCa#fgbh9Wx_>(z-g9NTfG|erYo|@|q*uy0I0|$x^+# z=j2G~_#_i|Tz#r@ju}il@^)xRPA=)ru(3tLuwBt%@C%nyHFSyyz{QYUHdCTZ|Dn4Jg$JboWP|uEUcO5baMNYMUC~iL z6vvyAhW-M+DFGiCywBYKw6q}M1Qw9*6l1}3Sqts%TYX9_T%THS5imXAFo(Q%gBWa* z$aJMn7*G+pyVxkQv|Zxc#ek2B0e`>vqhce=nf^aDa|CM? z$K6`HUG5R^ZmLt^c)2=JD%OX`K7~qLa|5F2HA&zdauwcf{*t@Dq~viLuQZQ`M0qd8kV>OdX=opEyoE!H2a>@Wc_d z@Lcu8p(5Wdc2Rm=7FFsN#c`S;psshzR;rE+>H!5WGfha`1~}B|u|@P5#c`V2;H-1* zHtL^Ln`vZ&fQ#HF=3Uce9d5@>vku%19=9N$>A^=!gMM8T`gbJPftNLSgF5Da-7bP%tCneoerIucX1^w31;(gY6eSz7d$!B_w3J#e=n} zO(yx8aHN`sg_4Zij z(FIA&nzD1QMyym;E8AA(O}E@wBdy_-Uss9^IowoUPklLESJo6-$ym89l5@nxt~9A; ztG#%vX{{ogubWg7*BbRg1MkBU6=@}V{p#*kjroDNN4n^hU%JvniDEpwRK)3ZFUfq{ zM@yBcUntb)g-^(3F=Zbb5HkyRit!uyR&l7y9xd%<%hoE{$=5dOlcmBqXCT!SI3^dl zOKh;{vanLMXu%mcj-v#7j~L_w{+SYBy6`ZWUvOQ?I9eXfkMFIN^g0j+#lW)LF%#-S z*ty>~S*8}nG1uyZcyF6v-Cgm~Kf=`Zx4xT3c$y7Zyg~Q(n5+uckgRAuRLN@+q%k zcj1Zld79Nc^`1+8A}fgbYWXxT`*TmW`!u6@>JR40_R7`rX^M67B~nw7915&waM)8C zSvxC{mLH4{=|9b zxs9-Mxj8d9b8&3mCVq2SqsXupr^OUNBsHD6H(fsXYUufUwrtugCzxxja$yR9TfDqY z(m>JW1JK?DwKxOH4_ADcqn;2GB67PJP}6Br-6~7vh(4pvALe{%=q-mp+#u}~8)~QX zEuBSbTWSz}+Ydr)x`vn^O^~<^u<5eyrcnc&-Vgu|e-NSv3_d`SJH&>IZgE_;T|(f7 zKL|0!gfl5-Qjip8uuYf6W}MN2DXxs_6B1MoMG&}yA~%T*65p1s@ZDay68_I?DK?}g!D@)8}X2?F7y8P zj|VhzL|l`usp}Mx5-|vC#VZ-{QQ_kok=_zWHCwvHBet*VBt16nFV|ZC)B;Gj%u@vT z&G!3R-=n%4O{YP#`3T&C{%_xnvI{XU`#t^+IVig<#H@7ly8qikuw{AAd{Wch>5(B> zilSq@?E{M;yo-eI(YCwIV`@`zkslX>V>%^6YoAHo=EH)!&Ey23w9S8$EtR>OPD|Gl z#7ZTh$XKlAJ6Iv%S4dY9YnT&rvY*dz@huxKb5W8a_D2BU|3<6B

25JiZd>rhNP1K*wzjnstAZi}CHgeZorw|KtRXCc<5y`+WOYpd0XQu2c}d+~Zow c*17eg$0Sl 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 RtV6z>Pw9^P^4C+k{{R8Lp=baA diff --git a/lab/Untitled Project.si4project/cache/parse/kern_monitor.h.sisc b/lab/Untitled Project.si4project/cache/parse/kern_monitor.h.sisc deleted file mode 100644 index b332160c1c74b3da7a06f1cc493424dfce6e15b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6180 zcmeI0Pi$0G6vm&Wz|evg1OYMjF&KrI7#52B)36~aqO?FU>cV#H%+LvCW-=|rjfqPV zH!O^C;erjx5EEsgiHXsmV#2bC!Jsj*5>4tq?9zq${C@AgGjsb|ro*CL7*9Cwym#)s z-@WIabHCnU=ZYX041&%b(x4*oA>mPQ+`n4>2@w@A*@m;)nX$^K+@W1?5c7WV( zmabPoZ%W^hb|u;=i&TH5~!e&}d<-`l3Eq)ID*Nvrp+AV0>xd`j6Re zn}i`zv>Fo&{?!1pbER^z@{$~+TB(Tz`?M5r-qYZjpR?WgOrnUGoB+NC@Kg{Ehk|3oq?TJZOkymwVSSH0An2NBr}Vh_(p7Nd1oH` zUWlDIAjnZEbK0pgOPjqv9t4nWLuRv&Mdjx1JX9gtQjmYF@Wr1CD!=MuVLyIF<r-7Df;!!1ZFjiMaRIk)_+&@>*L1c(Jp_1b)OV_zm&2yU+A$yy^X6<tJukUk?d^gO4_o@6tyKQT~GiwGF$V|Am-5yaoD_bR_PEb?!TUcEoeB p4#d{PU2jVBZF<)ob=E^0$I0(=MSbcYxIT8&EFNyCCX7e${R>%r&U63( diff --git a/lab/Untitled Project.si4project/cache/parse/kern_mpconfig.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_mpconfig.c.sisc deleted file mode 100644 index f4a9898269fce61dc0e64a0f981ab495107e5116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15692 zcmeI2dyL#w700LTw%rGhVhgrZ?Xo=F7LY|MtwL*|8j7KGcxeH(-Of(C6FR$-o!MFv z)J9O7kklrUPy>-hL5hzA0~i#P$|Lp<(f$L9qLf7A`oiiT1{2Nr{oH%c?|0`n!wf~2 zKa3|id+zW2?(cifJ@=gRd+ZF&$>pxi<+_%Nb2D?f%`0-b=N_5q_<2eAz89Z64&8lE zKX5#9R__1wzY+oVevx>A7IcUBZt-IAptws6J@|(uy_Flg`rNoDm#b*h|EDFr8)y8r z*G&k2>Vc*%_)G6wrQ^`fE43WGEyx*?*s)x)YvXIeR3~oL#CM77y6{lL##zdM(EMdQon=5qVRd!)NW$G?^Qn$BGz-l#PV>6*`qU)7u~ z!o@%H&B=MGj&mmj+a_Ya>-CdO?J!O*jTN>}juiVZ*5JI_&6Q$x=IH8z<`5N&vXj9I z<5pG@SPkm+Q<6hGJf*=(wKh>2+a@yviPKuIBPW9y#+}V5JpzHVsKHFRSl&K-^Y~;< zmNx4wjw`y3oD7y2ceXS?T+ zGaNY?Y%p$R1D#QO8^y65vN5d5_^ZrwrqUZKK5 z$YUI~B6TttVcg1yZJtgm!(JS@KEc$hMDzdfh6( zZ+n-Vuc|FMw~v>U$2lY|1yIvvL3JvjdObBkeCBQ$AFl}*XX#^O zEmr`wm2#RW7%1abptdJE-5h~2d3vKxu+q1P@shkfO-#!$UDi|Ap>-ar*V7h71Wu?F zCj`6crfO}ta8q>yD)B-wp#qxV;+N?#Wi<8tKp$%~s@K!Az|SmKu90B5r;C9+Ukqfd zOD|H0UK;3Y^f*mEAm@}Th4Hbiz>)$Nh=ILG3@kZkx)}D*@3NjY!B!^53mbL$N#fOF z$jML~jM>2SLT32=@FU`z`i{4K>+|`mH>}-s>4pv0;xc=5Nto+LMYGR$($g=Iw&j0A?B*j>IFpv@Z54`Q)#SJBRcF+ml)cm=hclDpX`G6gaZ2|P2T*+ zCB27N%<044?w36-dw@SYuX1ht45vd!?#3L(0Ad&F^t`%7hUUrkLz;Vocx%|;Ug;kb zlNb2H4q^jIvBWysQ%tdc4-+-jv* zm?-Wb4ztB1BDhF!z%^YKm+Ot1;!qeZ>c%-)zDhG#$veaVBe_!pY`QG4P9@azz{48> zy`6O|5d(@O_W?Cs7L?T))$8dItC(FX7bYsifZgmDgNn3VV2mz{ibbQQ7)%ta#fcro z5!MTkmE3-n+`bKq-BZmjDZe9chE#H|{_Qj}-jy7=!gW@1=;M^up`*CunQxoYspNVD zI4Ij?W&cxR%5J{o_oSyBe;Rq`o*^Bd;zYGHK1QY}cDZ+o!9{|@CYdga%kqqx zGR3Ne@>Vjq4+;PeiY)~hqsszIl~B{oS`Tj{lZ6@%Q-w>#v^k{Vnl6jm*5UDDxi-9I zdy$kNM$5!(9CE&xNSLlYFiY~pPW1@b?;SNxHJ&X78EFmFbnTG|q$iMP7sqN7H{&lB z&k+*{q@}RwvZ$=Zs40cF4_8VB8a`WxpXWvrB(64H7P&FR#L*M}&fV$bfXCc>-zCO# zB#+DBnl6i*#?(I(X{sX^;hmOjBn2@zPs|la^VoFlk=Y+lAa`58BNX(aAu+&6OA6Cv zfz!p~a(d)K!Pb?ERkVbd(sQJq`SbVrN0*{vxko%tx`pR0N%|<|PPnX(f*1EBhoe8~ zQhY))59-uM3F5=ri!SkTvVQ~nBKoHIDlv!4#l!(gTw+}|4zX`7?|s%P-xENQRu`tr zf@(@aO|6myE{(8ftocGQz9NYozM8H*Y-Z0wtE7zCFCIj}MRLyq*K}Fj#NoIDT`2Rh|@#pD_iB(64H7P-;iOdPfrlLt-CyibhB z$OU3>O_#-O^f=8>_9-bg!VnilMGqGTLQ&i=gnx?H==<}|KlOG4$n*SLB~KN1DhdK& zgq_!06dN^nhKzkn@*0ea2rR?P#00{mhRJxjj|nPAjm5xKp73MtWNECna#QUpjoE;8 zV&Z_b1T$TKRNuRQHb6T~sHw$a%RpgmhKQ~`V#Z57uXAm$*m~@|KQHNh z^u4~vUMKyCxL>;5V~hI;JFoSY|M1i$-ayHV#!w)dsMpoQ|V%HnuSc~V%b<*tfe0kV&ROJw_U7- zHl&|Q1%Km>&1n-ihK=1XV-+oG-YZLb2L~1>H_wH-<`l8@34DrcQiqP-XrJI-c7;Ii z3poFf+#tpd=css-8YW|{-NJ#o!KW7ATB$s?>{>B*2_(zHjOqHL$~(|h&CK*fU}p?l zf8c6T4#|tBA)@P#5Jz9=+#gstRmZ)J$1>z1F_@;Cotr^XCzSd{5YqZ`Nm{O0gWz&p zLJo-y5}op*0W!@TAk!m8vx;L)HM3v!F}l*%%#E5u%^Z<#?+<;=@QuzKo6ef~i0ra1 z+fb8ICA#HpYsU30rn|<}YhQ9U)qsslXN}6FJKH8yPLDn(jv%P8)lybb*A+2k7_&j*L_Hn*0StH${+fGUzC&*Ijs#BXtZ{YsUXVyFQe_Ft! z+5}n!M4Px@Y^{Qn1KXsA$yn=Oz_C%>zFwnZ$kuIQ!hp0#cGG1mMpF`Ms+cV!!?Yik zIw}SeX}4+9wZ{zW)^*^_s938g>4;W=v}YdEWm|>Spr*EJ6*xk!PKYtLL(Co(i%VPk2P>(wIeCT+=H4O(i#;@5 zY-^pqnWrJZefp#)uJ0;tuNFCMNL&@^!tfl-Ig(?q`cm#m-Y# zV&j~^C}i{Fr1EFdzp+o7RXC4^Vqyk9ZI(|*LgT{DJoOIu_$#-fqp3vfXXE>XU34y1#(*!uYRZOCo z)G!&hFLom83BSTWOzELmx=lzDbw{wdB5(r9q!~6|1WM#d`>(sNSY9src;!Y zT9VpU%CI@V`OmJ^{O2`+nnzmAn=T8gDG9Y`c2@JZYZAWg5fh6pvFYMEJoKw<9X8i| zxpMAmyK}BuHFV1@C{**+&YT2b9xRoqyI@(ga(S)!kzJy+>^8GF@zIoosbl*B}dXO}QB4cr(n zJyE0jDFz4eQg>-tE5j2l=iE}Y{QTqo&cRZ-b99Mqsdgt|eoS%r(PI*_`LFlRk=Na9 zD!i?6JwEM{PeV_qK2cuazga%*Rh-s^l#p5H(|+mpM?O)8#OlrR=|S1wnepkMbO)oo zBSlGqH_NAf?cITlPlx5xt{+qRBR^jB8-`)c@S(mAD7Ll>2Frz7RM#fQbX{Ja=rIAFTi z*r0wg$2_B^f9C4V=QMulc)e!yh+hzM!AoKTHeC$7?hn~V1CrAdt2j=;JWaVDD*(v$ zS=tO8)u+o)&^JgwBHsKLHRJDx*B$*lh!t@iPEFVS`NP4+!;&oa$6}mcBsN{V=s`VG zHWn=2dE&aB>L*II!f5}p3VYPBVy*5_Sn*3TwL;U^NOxpUZ#P%QNx`bytvI#mbAjuj zPPggL$nFUOFNuF7K9ePjzAOHyc%vBGkBB*iw9g5qQ)Q>ws`HJ}pfo+hDGAlOwL zj}OrP$C;qh;hq+Qjx==BWzicQe=T%=Ny$c1n$L(qN80VkbXj!f8P)5VjSRlKpJ@V4 z+@R8!y;mPeaCdDP0QxkeM^W>aYN$z@PaUUB`vEu}*#VJbC zN=%0h6vvMf=9JBk%cS8#9mGE6<6%vOv$|tFK2{&H^W$S|8)x-)ob}K2v5e=$<9LpZJDq1<+d0%I^$S@ob7y`~Kg>FM zkADQQIecgx4D*^vXzAg;Z@jdHn*gXF$862_kn62U(-pig??uH~OB&?mfb zIzFmFANRujy*G^Mc}BedKtJ)qUHfnt^PU9Z-yt{h?@kPLlXp)RjO`pc9z&rgy>Rn* k!h;UFzaEcAO6dDuI2(t<_-B#sg)lx6=`IUno+^$04K7l4-v9sr diff --git a/lab/Untitled Project.si4project/cache/parse/kern_picirq.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_picirq.c.sisc deleted file mode 100644 index 4c3854ea4efc71ba81b844522505fa0fad0ccf52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2796 zcmeHHO=uHQ5PprRUDvj@SXxBE9J~lZYpGg6M5QWTL_q~9G=^+wmuQU1Ry^h){@^8` zqM(pmD(J0(_Z~cYw90Qna6AOA`P*!x|e5hU~k^b*tuvO!6Z!M&D! z;@Mk@)GN$LgLJ=T-#PWs$b2KEbbyt7-RgyqThZZ&0mjRq2VcgKS(3ByGrpz&ear*E zJm>>x3i}y2T?fx%PqoA_d?=DpHwcO&9!KlZP{sRT zR2VP1S+{fxab{t~{<4AF0zL^&Um}w`Kx82Gm4I%!Tx|T zqhAXJNz?HhHVk>jsLVgnIHuFHfs9OQJ@Kt>#}m+c81)`7mUKMKdX#fsR1&GIysE~7 zmfd{rzbdB1&Q~aVTC-?d{aUw~Dv3Aas{(6#RooNt&hV_1aJn# zzJWapO-RzxZsF9)y6dFrl%3$b><;)|a3(0S6lM-vgXVhYa7L*ysU(NzJQm!X1F<^f z90@uV8RVf;h})nwEq%~WkIQ|$H%0WJ8uaEWbPlMBL2Nv#SxkqcPR?`m@TUfmyEB~c+@Frh10lcWPF6rl2>7I~CI-Enc( z-KoFF`nU39?b#DT`oj?)df2k3XP3>7TWmOhahnuu7Up~#d9d=h=}-P#r@0C669M`O AGynhq diff --git a/lab/Untitled Project.si4project/cache/parse/kern_picirq.h.sisc b/lab/Untitled Project.si4project/cache/parse/kern_picirq.h.sisc deleted file mode 100644 index 7d6921aa240d54cae7d8356bc38fc398e0945968..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3515 zcmeHJTW?HJ6yB;uXQ)e+QqhxfNjxb*+#)HN#865HsYo2doa%|1sWVfTH*fp^5f2hx zB(8}_Boc{8@ZbmV!h@{;vdz$j#MXYqq+I#J_zjfJb@6)asN*z+F zaSd2CD0OAFQtzHN7=8~)zVq?jZ0hd3zBh+*y82K5N(FfKOTY_}&^7Q)@O&`mjbPHe zZ=IB)!$QSCB><%aZXwg3N;Oqex#RVZ*-3} zHKL_SskdMU{!c>l5W(#N7ol?j{sw#)ppU?706Ge8vQB@({wn6jA?a6q8ac17n}s#N z$0!<)){HnL?w!AgPFBdm&f{$asVU~-)fHrbDBymKy5#uPuYgEsZHEQvLbMDzf<&3*| z{l+cb>_t8%i|u|&DGi$lJ0_s$lcKf67FNIAA1D>Q0bc9) zOTtuG19^ibe>$Kp$q{uz6H|#h%J%fZl%Yy^at(<>-P9f=rZANzuT=C03fMUpEQK8Z z-Y22Lyhd6}V~3~){=DSWQGad zAuQwWpbcnQC=48RAuNCE6rD{1OB1%cL@@lNGj(@s8!xfqRVaclL#Lj1q-lHib0$rD zX2@{(IfaOQPB|w@A9BpS(vMSpPHBJ8?ulMQ>Pe#8P=9EQKK?nBj6%PS=gWSP0Y#I& zjl^zj3zNN%HU2~J7`n0c#j_+?URhqMsgjlCU}_SV`BcfW$RtTvuAoS+$@q^GCMc68 zfMexKNG2yb=*?+ErN%yF%qNm#lGfv@$j648qg8x~iuG>^t9TS^Y?)8c9eCA|rgJ`I z(h8J+t7TZpXkwBe)U{+$LAf|o$J%zn%RSalsWTKL#UJm{( z?Mi3N-GJIsgI%skS@;bpdnPljmzeP|R+7Zc=dcU^7PmhLPgrc2Pt-=i=EtqjMsks- Sln=6akvdSvt3i_Il)7Il8AmPv diff --git a/lab/Untitled Project.si4project/cache/parse/kern_pmap.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_pmap.c.sisc deleted file mode 100644 index b9e30b0cf93c8f8fe600fc06556cb062c29342c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51788 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 bb4;i|KWfL9^#IR{+VOoA0N>dFAou?OZ(Y6& diff --git a/lab/Untitled Project.si4project/cache/parse/kern_pmap.h.sisc b/lab/Untitled Project.si4project/cache/parse/kern_pmap.h.sisc deleted file mode 100644 index 0ca201a97f354d1b5296ff8ccecb100fc72232ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21284 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 A82|tP diff --git a/lab/Untitled Project.si4project/cache/parse/kern_printf.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_printf.c.sisc deleted file mode 100644 index 64df07c329a413c7c121f2c650dc07a60dc3b222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4214 zcmeHKJ7`ov6n$=@SvN+5CNZLzM~tAAf!O##EYl^5f}O@)<1T1il1;!arGkZpg@s_6 zK*T1EAc&x#U|}a(sQ3eM{Y5Jg6rShI%y~O`Y<6Rj6yk-;opk^&I;Gnti)=usBLAelq;Nrl`JIb|4Mh%P-lhf`c^vJNvo%0UM#ROG)k=X!HvQ9C5kFu(0=2@xk7FL3s!)2X7Hp;Bn^odDqG;?%gNfSem1C7 zHa`e4>gVwdj;i?Lg`Ua_Z<8vO9jm7yJ5r)MerSSY*9Hz zZPjidunkC#AG9Ou^=d&=|KIv@z39c8*+ zT{&;RfXp>PHP%HY$~~a0ux^`wT1gCI=ZdLYwDs@p+4@aEAz}bCM@?n>-U2web5@QxA9xB*m>sf4<01l2xN}M3%x-}KTpnHVo1m+#va=g+a_oM)w9 z6rsi?sommL=D7!lSzlr$6*hrPB5}Jz9q+}zrT)bI4UvUF$Exku+LQcZWF%`SeP%oa q#m46Nj(7L_b=#+sVa&eFEMn`tX=OA!JyhgCFN6P&OK&FUL+}T6)VE*& diff --git a/lab/Untitled Project.si4project/cache/parse/kern_sched.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_sched.c.sisc deleted file mode 100644 index 6f6c8a5bd835640904f51d36b80152b14d87e17d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5175 zcmeH}&u>&!6vrR6Vx5Xseg%TY34unbNaIf;lr#|%-H@s%CPX`>GuQ{EZ8`(NxTy;x zsEH)F(TGk=h>8n}zgL6>3lqCAAs81f_!HQ5`TTzFy=UgWX=zIc8{}|Kaz%7ld&D{DYVwfXpA+U|%(Ar&DRHe$$A(9b zl`F-aT5KDtRx6XWwvk;WiAcv>IU3xsp3jZg4al-pjWupo<=mXu{;Yf)5+50U07rul z^WBAiK9ak@=}pR&zbXtE*_KMh4p-1T)OB9 zvTU7S=okLFyqBZnV`*)CQjYdZ$bf?YlmKzS8fPnn2BoQ)Lf(;G1MjF%9UiHa2Ss0JTx5Z_rH~mgp87&hwckVeSO%l3-?Z<`N z1J85(ru6&Y1T@oE9x`g`6q zp%W5MhKO7=pA7auJZ+q{N-pKecv^U%}s%d$&!sL_fMj;mnYSVALg^f1v2X;EEwV}z{ zpdO&Kd{BEDxhYU?Q=5Yu zrU%Y!NDmdMb$2(vTX(5AgDO+)Serm{!fE&FNl6pjU-wt|l6Vix@PK$HmY?ZBH_^*u zy2QB96~?!L#m|1REBs10L}kfqvk+g3xhAUrRQNbCVdQ#QAE)1b^7ZFlT3w+F zP4?~NYEx}_8O5iq^@cY8l!%|t)uQydF6)&-uB%h(;8f@;GBTYIZ{7FJuF_3vW?i+( z5i18xU!HAsHvKrV_+Yy>@uUr?`u$=0D>C|tu&(NFKX9?}*MP4iUqbs@Qsw#VGm!@P zS!zt*NS80Awb{Os_Rq*DwrzdOUrE|a;Li&Q6iSgPr14rGEr}X1=g~?BB_qXMLehOf zhzG11FMhPHf86?Lt(2w*?5uR3vD`Oozu1`GEhNwvg}5LAQIp zSSZPzifNNEBFtGk&Y>JF=%=^b@eYa-E8rwfefU$Dx_=LHO?qU)ukoW@> z2nmTc1r4G>qNAav3r*5dfB=afK(V~Hd*hrVOX(3$9?$N~&d1Kq+__PumXs>afmA`M zC)jVlFF5)FPTJbt9;a;U)0+wM!|Ff&l?w3fr$NVYp%l58WHsalSHPgJEj$w6hRcwL_fqm^ziFgjeXF2fr=9?krdp9HZSTr;}~kl_1HwJd9LYxlPFQ%+YpCTKnm zO*aUzXAAIl1oZ@cQ%1i3)^Zjq_?{wjpzT!Hjsnx>T?eXr4?5;4?dGbBJ^fQQEDmJ? zYb&yJIacABPI&Mb_165Bo!RTnxHqzhIyr4V6Eii zy^cDsnr8Xw)0n>HOMd!pL&s;Cr|j8SnP+FeRm&_doiQCwAK1Y3aZlu*hqm&h2TdPs zZ-Ho&Z#BvZk)2nHACf)>r%WE$$1OgJ!{98A(t;$((3*jSSXYvmzr&~J!`EqdNr*EP zGIgJeBF78Pr(`H``L_3e&XnqJ{cJEUbu3o?%u~dD{>q<`T%<2J2U1+Q2W5W&vU;iQ diff --git a/lab/Untitled Project.si4project/cache/parse/kern_spinlock.c.sisc b/lab/Untitled Project.si4project/cache/parse/kern_spinlock.c.sisc deleted file mode 100644 index 1b4d1a61f267baf8d0e9c39be0146fb240d55856..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8508 zcmeI0U2I%O6~}KKw{~_L=c7rJsuXYBD!7C+cA!;J(liaA22zqxTqtTIXYF0bD{pMe zAIJd-Rh1CsEe{0=!9yM>O4UC21u2h_ibNhjDi1}KKq5$dgwIw2p}wHY|2H#d@9g#U zdWn=5#F5TDbI;72uQ_LC?j5-+Nyd_-_hDhulO#WTAW7c&agW1aih{5G`klMsz4nvW zw?H=~|A+sT2(b3|33tk&SA{IUFFMLo0DW!yp5lO3I`mL#Ik+cK`q5*#WOJcmNE`X-ok{Ytbbm(^{wbW$`vZEP(x_1h`#u5% zKNt2$*)N3Wg`d>eXXMD6!n-tQLj4R?mjg$)Wjy)@LZ6Y?rZnAUY-jtf#_VKsxmK?n zQRCp!#rgVT&-BqrjX|=MJ8(2;pu#Q zv^Q2`%i-nR)23$S@yDct03%DWbKq#uLAywY1(ykFs`Ltgt@pL*xKO{)tX`a3#+ym? zB2laAz|o+Ic9AA{2AzYpxU|riJufv?jYnKxnCECvL%T>#yF=852OQeja%iT$Fne%v zemO)9Vf|sCqd^JnA|)1KrmP<|r(|)yG25J*oROxQMk7%*>A=yTiFT1Di!c+?bhn$w z19x_}yE9X(Pb?F*=hcV2yM+?(5ZPF5yx0S=TPsOBP0J7m=qck!y*Z8>FX)}!g}@TE z2ZhK+PgZW8Csxw`o1wBrmiIIrknLM&Iv)nk*O$g8tIcM8VSIjaaZGHK?-L^aqe3(N zBWfGK15Vr+%52qpnlcNc{j+a+u31wuy{LCwKR}1zQ6Vx2(<4Fy%br*MFjO`j^rh)m zJ$=xSzIA+@g&l9qHkJs>m&C)V!$RPckokn^h|s`fUis5dIW{~^H_K`)^mcD{(vi$! zvdeveF2SM@DFpsmp@B*6mt-%;_pq3o5?vJpSYCFLmt9U)^^RYDIkMdpS@59w9oELG zOHs+!*Ho=ORr&O1jz9PG_!rN7_S7?{KXno#$qp0&v;l;}D^g<1z_>Kcauo;etk2dO zQ&qLL3ptno+Z~Yx&r9W53|*U!u>C>C#{xwxydouTV-MqsO~)Nd$VT4ae12k=tCCfT z{FiX#uY;wxd&>Q2xc{3G z-I1b#lD5b+o`Ml?L+Z&OO>;8q6Y~@>=3^s)gc4k28n3fVMtVT{9gVYkBI$l1k}Eo&n5`L#c^F1siMU*W#=tlO^jN?)$}8rOPt399HAvXw(`|8iq? z>EnmSmlzy7yiLyaKjj^!4~Nf*xAJo4Fpgr3IcyGy)dq}-p<9OuRBY2R74k}01d5CT z8^Na1?pAS=v&i45xXlI)ZM(9X4{^{K6T_HZs1y9M!__F`tlhbwGYe>K(mJq7;4_Ca zw@0Yev+*iXR^MDx&(wkkWcG6AVPqV{`M(X8o`14EliwEa>P5e>+yXFydC=RP$>x|* zscZ${zsh909FmQ(!y|tmEKMKv4qug5uga^-a`@Wlp!;0oNZ^p?{Isl5v#@m8UkuP= zYq(L?Oh_-;O8H(10_;dQ5Z;aQYVFP$lv{0%gwvYLsy!my8|Z_gtfuiQQ3_r8UH(X5 zwg;T+Y0h9LO{Q~DLk|i~B)m!_a#-;cs|9JAvvan3p|mSSYOtZ-{r|rOaUr2XCjtRzNssi3tUa*V*A* zVopw17pU-fbuY=W$HVUNVJ**_!b@7+k>MS!Gly$D_X*oQ2w2hT_K9zRo^Drniv(U5 zvU5y|5_G@{0G>9e*1u#pZ;+971TN>uLG-_4hMmc%%y9i%Ek z^QaJb(i4}h@Z{2(Vh=+c>AKii&!@u7v=B}{o9sKih?&cAH@V}*K{7~M#uhP9dl$Gv)lx-JU>8e>+x&*|4V0T-( z7o?Z6Wv|GWE#(UsjoY_ew$DlmY+G4?Gf|O>U2!~gs z1fD^6wY93W+LNoT5%E^O?rV!5NrcXAETXr&wrs_X;*GU$skSgY-b_|iTf@hFZEaP& zzAHSYi}tl||D%MXILsVEN%%WCoSnixK+FR+x>Wzj4|9&OWul9Jhtw*R<2L5Wj%?>p zU;9zWNDt2L1|N8M@?~K;&>f~U2M~Hr|)YXf0DvPMtJCQYBTv0dmEtc7Z z#r!VufSf(?Z;O5Vq~V3e*`nVU=ah?A!*Wr&HQ_bRgKzb@WukN~?9fijMNkYd&gN~j zT+Fc>%UQMgn*?RQM~?CGL2X~AELqUo+nAl2dq|C)0nHe_I-v*#mjbq?%lZEIwzIIe z2t7em91i2TZEZcdEfdxo5b+Dfw)ExtTDmp8H6{|(YDV+Ao)ac_9G{mPy|;8*qf?)a zS%-G?&COxG$p^slf@-_QKfE{mv1emAU*a*V`ki#?dwz_`cnCoONLFC4C+>*d7ux#+ zh44uL*!cxz_W X$aLUwRbU4KraW=JjfJ+2N5_8v#G7!n diff --git a/lab/Untitled Project.si4project/cache/parse/kern_spinlock.h.sisc b/lab/Untitled Project.si4project/cache/parse/kern_spinlock.h.sisc deleted file mode 100644 index a3a4091cb8a3d22e0bfc28119d4f1ec7b4a98a79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5818 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 YCPZ5AuB?q^ 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 diff --git a/lab/Untitled Project.si4project/cache/parse/lib_exit.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_exit.c.sisc deleted file mode 100644 index 3bd1ca9229760dd54f87a3d9abd94408fc6a33fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1450 zcmeHGJuE{}6h5kWy+I^puo)_0VlZ?Nb4L|e~j3Qyt z4t5f06JkV+M%#0~``&qOg~2LLa^Cqr_nz&c{@FCJOFCWNBuOGwi!zNc81jCB%f)kg}t7Gp{&256JoGVXkPt=&mD( ztX=L5nOW89)_FFCXbyjh<#%L;3avO=d!Z0An zugjryHC&?PAs4l>91ME-j027>UhyVZp diff --git a/lab/Untitled Project.si4project/cache/parse/lib_fd.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_fd.c.sisc deleted file mode 100644 index e9674759183327becbe265a2236684fabb017874..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24174 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 Fz)#mUdPx8P diff --git a/lab/Untitled Project.si4project/cache/parse/lib_ipc.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_ipc.c.sisc deleted file mode 100644 index dc1616c22684839efda5652a45f7739d5a08f6b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6019 zcmeI0O=w(I6vtoF*o(Kc!+E5f7Z4`_8@Z z-19%@+;iWgdsz_l1ws2panKe7o4bPG#@B5z|0W4v{o}@RbXR}+{9feJ;D7qBM1b5s zAYLUv7sQvv?c!`AC4!shZe=Im{t8tvx+DmmR-MdCm%sL9tV0h)34MEFDx+obb3CCx zdL^d263IOKSS_jS0@Yx3orZO^S}zr){a)NJ#)jFAWoN2mZiUz<+_UE>T4}@Bhn3}_ z<0nrSOW__3HlHjXYa4v-6|KN7zCjzr*d9jk5tx zQ5Hx)4SI}8U8qXzVA`* zbd-n2wr(kmy`eE!$kP3Z3?&Js>GYh@V@$aZW^2kJElBLH7^n=NBCv(gvEfSTuvDlq z1!Uw?Vl$5}^vs9v*P2f=iJi8Q6R$}SaZEjv$la1=5?$z-?EkQ~98v(7%#M^Qr<}Kk z^hlx}^dw2dwCOZo4TjX!v&uM%Sm_AUODa73+R*QE;N2fD6 zPBaMc2Ex1c*^3$c2QPlZt;GA1?Bg5QtPYWo>6+uE1(OMn5} zDX^Dzr(iJy@K%D&%f5lN6M}>Q%eXmMDnPl3=iovR6c`W>W zqNqW_k7yam<2t)9k!vLJ!E~Y5AHq*+yIFcA0CL&bFs+h&@ra4_iF{nr%%TfDvom!~ z(ju1RYy8cVG+q#3b5kNWOPWn|p=WbmeVsX@C<3S>tgG=7Qpy&S+Y`A{(oCWYJ(Iyl zwdiX?O5>&F5|kZcj3ce(nofGZM6@EM`j!iFs%V!Oi~Gf3xkqfeM?8yDax=wB`%Ir= zj-}{r<1Rs z+5}Gu@I(TbO#wWkr2oD%1x#{DUMm4x7XzG~VkBg`=790i5r7+BTk`@ooq+ArBEU{4 z0PiMuG}TOI2~sP;+O-8R>n}*ibj`s6(GerAEfVCP+$rIjpKvGlSIzAfs8G7wKmHt_ zSg_ieSPW~rn=1r+UhL!!I2VIslEie67(a7jY!GYH`Hd>LIGbH~MRjSVBi`#O4}_Jy zdpGqKUdZY!$@zE2FI9x5TS zX6B99=ikKX3$5o@30(mD_KVx3R8^f$;wqkvd|&SL>wtDfOb?3rJ%xN&%wDDoy{mbq z?)xc42d?(9w?;~QLGZx6&(T5J7li4gPdmTn+0dx&3!==dpPqiDB6n`#bw97tKCsK( z2KEmJpy5fYuX_uXNs!pMd4sgp=KSpF9UAXb1RvA;o|NM?H zwTf)3R@rZ4cd~|=)F=1EyAr-V;;6nL!jG6$4s3X2!$mvC7;{ zt*Kh45YM3fLhc%`wcgZu^adkgr8M=RQkopYWv-gQajj8%Sg1Fn4)sL*YEDFlQVZti*8=RVN-s8$#~+#vezvgMdN7@Aumb|f8pNE& z?{#h8CakNX=*JdGH>zcQeZje2_6F!q*QpoS&&}Np%P1uXlpRf|CDeF@xMVtYZu56@ zGMki^rf!d-6bmTmOXwL<^B!IyqqMlWz0x@@Q*yIcRVfCa$8P~GQ5HJ!s-@R{=IMWH z0i2;ch-6d39xyr9*g+n!(rk4nmQYJ*VS!l&p@3yjWjPsh;%`=86q&W8HNwhFbF76$ z#g)WLOAzazc=?euRD*!1+IFwz&fXiN1Z`^t-OJvfvA|71XXlOwi>C;x9}HhX;}tR^vh;KN5vk7| z3iMOzm7dBdg;&TZ{rr7l^*9-cy zHN5!DKcr&s6zuv=<+qf>AAAMOZ{C5y$F<<%tu;T2CSw#iWR~Rj*P&R(W|qG@27dsX Czg~6# diff --git a/lab/Untitled Project.si4project/cache/parse/lib_pageref.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_pageref.c.sisc deleted file mode 100644 index aab01c04d3ba35505db08a8884e5b542838f397d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1735 zcmeH{KTi~45XOh|^v=!kCjkqH#T7PGT2N3J;wLbWNI}P+JM|)g4FUWFA~jGbu{aAz z>`oIpO(^K0fr`YBz$Fxy*sRZQ_h#8e6RC-lJj}c^^S(3t%p6bdnY}P8jtW*VyS!_5 zdQgaQCJpw^Pb>KLj=uDw@7RC2EetUGVPQxgv?F{Hv<4OkMFFX9bb|JWZ@BEcFw$cd z%J;0YUB>4Ub>;9)v#b>3=rP_ze0PZB^RwU0-XT9LH63OJ?&f6R%15j(7tHq&MT2RS z+JlXyg~hd6z4}zf8txfqJ90$~Ex0?YyV59%WtK6~ptT<4|`@9LS`^x8= zCk3x1Gp>n7oQpBDp~RE?&CV-|ylS_H#?tEZ?p-3x1>G-D$9+|^bdOpW*SRD!5%PQ1 z=@Lu-sI1i0uu5LWm%b=>977|2bQ!MJxtpELiKfu{m`r9`F*dI%-^ujnGE6XWG@utI zBbD2D&WJOI?O%<3lgUhF?l^nC-OipS@{spj@)Nn*3-XKW%Z=J28O#vibfGy1ML>F8 wKlkyiAGtNphGqX#c-wc!>oc&{yH3!Y`5M3NIj+eg$MjRN*(Apabs`A=1~RCokN^Mx diff --git a/lab/Untitled Project.si4project/cache/parse/lib_panic.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_panic.c.sisc deleted file mode 100644 index 19d74f474ef36616a194b7da69e6eab2e32cb692..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2435 zcmeH|ziU%b6vwY=3^A#a(u&b4i9rOX77-jQ;^=0j72HfrQt}|iAXU1#x(bdCE){=( zxClZO9US}z>Lk+1-=Jd$;rac%_fFnxh%rkS@xaNs_uO;7_x!pqvl-_Wol74RyOeV` zv(BwQO9lK+5?+13K8|kn&BNWuG54STl?u@N{o*MlbVqzwtTAvoaatVE_fqcS++IzF zb8F(_4(H0!wQf%Yy1+(1encxrf8=Bm?ClIp$YzIgbhbBNBzA+SA!SvJFAN2;5bN0Q z2~OglB&P3A_71pv((BaQ*Bj09oE9@}?{cbjdO><(v*e0egF7MKB{s4^Z+)!fO^5XT zjQqy~6?bGVb!#23zEjI&Sy zNUyj@+%sxf7X*Gm_N4MCLO3tUotC~yj%|IyIVPq@TT9#(gOu&5LQJ`l0F|5Wp< xucmo7qma3cV*5uM;vi1Q;-%OHeZz~7!>@|jn8Xg7s+x87NoZp)Tz|Pe`VH`w?l1rV diff --git a/lab/Untitled Project.si4project/cache/parse/lib_pgfault.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_pgfault.c.sisc deleted file mode 100644 index 0a8818c86bcbddeb2231de272220834d5e3fffd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2569 zcmeH|KWI}?6vl6BwIQ@N5~)@NBP!}-1_wbaodu;rMZrH!Y#JL3HN?irS?!iVRL~(E zIyg8eIJkA_=vpTiaS+6zTgT`3yYHQNU#MWAllZ~Ox$m5F&%NI{=aS+s=cb$+I3;oY z&V9@{xAvjm%V%k1`ODf6w&m3~!{{CEKmL^p(EBmTUJ-gGc`X@`WD_mr8jm-!E3e*T za*KV=UDBMmCmY$x<|l8%4x$7r_c|G7gir6#4_m;_SUIKf=h!US-f&M6+EuGsRV2r? zxg<>vjjxB-x!*Q?yAz+OFn1K?&FtqnUz?w)w<@!R^IDv)*Y5PsUAUt8!rTjQfheV%4~(fotih2ArWhc)Mb z2@OW>MLaB5^-aSXPdBoKYfBkoi{Cm~ii)s@VUbD=3*W$ohBbS)GAvh;Jz?`GGBDd2 z=a~C0(gR|Q;&TTc-mg_Kg=9-2+fH6aRW}^djdLP<{n4^7BxKpc#f>aP!h`hb5q8@4 z;2_ozN2Y%%GSBR7Vm9K|bkn$Py=CH4+Dtc?nuCcxCk+|16`b>(l*zS!_C5uxeO!#e zWI``1mf{?cPyjSfnY?C`y_;9&6rza8806_|&ghr&9pX!JRKm?kVYb6T#`gE#nIBbT z3^I<^s)gQ}A0IE0I-G=g!!U~+r{8qKlXfvWLHFNJsEp^70KkH3ZOS`WJ$y2jjUZ>rWA+? z@wa4;X%mkZu|4D=pUFwB5$*sA`XigT`23I46i02#uN@gC+sqdFQ)@WD4H!>eNLw7{ hZoi`)o+mzP9>x^apR+gO7<=;6!C>8|hCuJZ{Q{u!7FqxR diff --git a/lab/Untitled Project.si4project/cache/parse/lib_pipe.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_pipe.c.sisc deleted file mode 100644 index 7edbeaf29dc3c124eceaeb71bd8585482688b470..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16619 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 acO?FMg>Mf9ytTf4dBCHL0hc)v&-*XjwdxrF diff --git a/lab/Untitled Project.si4project/cache/parse/lib_printf.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_printf.c.sisc deleted file mode 100644 index 4b39f2efbfd820f96a7aa968da19a906a26fe056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5213 zcmeH~PiWj#5XXOwiCH(T#58GRRd!udtcQ|Kwe=54$Vqb1R;f@CG0ARY7Gk<2o6z1o zddxxaBG^kIB6t%7FBLs_@Y+&QFsO&%zlI7bD*XI@-g~qAb`v%&B|V4(v-5lN-uuqX zo0<3fZSn3TIh`b(kBO6xB)QO+Bv(G_aC}t~zVywNz347|{{BAXUCICSUpWDCf4{h2 z0lh7LSDX=N#hqg4@{hUf`6mZZCx56$7B$iPb1pma!y%T{DGjtfeBJ38+N8EQx+lmz zk^ovQ+1_|hSn9-k0(e}TzSL+oOuVDrglCJ zR2E9}%jL8AQ9TUTE>zDhb@71kNGx3W|#SI)^wNy|AGJ1ZxH z6`psta=;8aoC8g<$jbEM>oPN?MWonr=wvX%^Uh}WYn3P5-QQ$pzA_^lFG=4e)~(`F zgJa;vIu6w6i|-0O_KZwZcf?MGjZAg1vQV2@o_$dRU0sQ4*8vwK0mkt|Vj}}z`?k__?YdMw0_xGFZ2+eQJUgb1Gqcj;yNK)XhpT<#tGl5|40u{NVmH0UXozk=gv$RkH6D^kj=Z@sMz)#cN5~fO&^NVwnC=Y|-8hu5;83mD-?$7Htve+Rv z*oG}P#2KH5hK`8a2s;+h&E$q6|0`^MK@!{6MkblI=O_FTqjD7clctR-!H3S3bCvQ* zJ>WQfX#GeBVaRlOAEPu$GWUIJ)3e(XpdF>T=OuV#doIYlq(P$7m}7G^ss6ENWSX+l z6F7TjUezzYKg39!7h^Wo3AK(m_f;#RuF60l_Eu)|Ge^f&pJQXkj-NO>0%)B6;!nBk zS9lg!z(QvM)gqvn*G zHEYR*T5GHq71v}a^_K-gti6i=@@I`$4P`fDrPT2)E*nY(GF|&v>-l%(SAYEg5GQwC zef;s$KDo>h@WijVZ1PDXU;?!YBe|9=*2jdyG1)=c`ZyW!I`CtkR)5K5*FqAW6X04C>p zSj=6G9266a>GDx0AGy}r$#%k_pl9ExafP8}e<4WxGfZ^(2y^3VD<&R!0;glT$`wY6 z9u@N{LGoDyr0KdMNU57>>lMb%IN?(&!ZV}32&w$CxTqqmh9cx^1-D>4*{vJKUMhC@ zCS|gokoH}Ncb+)B&eGiUVr#IQs^pE(ac8uMV5g*8kAk(l?q;x5HonD`LUllLkYwr0Lp2T5*O;M<1c=jZ=P6>CHUV+|Ru#gHz!x%AbTg+0Sux+2xC&ZD_zNUO@$?cNCZ->)5*E){(w4009#m~3AhjZcF zAUi(_@J)Sk4SwKz!PcOU*KG1x_Mu;6{W%6upuQmKH;CfmUjX!o7w(sDX6Rur+!y&! JcrFHs_!l9SAb$V= diff --git a/lab/Untitled Project.si4project/cache/parse/lib_printfmt.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_printfmt.c.sisc deleted file mode 100644 index 16a168c2e89ee95c35dd81eb754ddb7cc1c6274a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21884 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 diff --git a/lab/Untitled Project.si4project/cache/parse/lib_string.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_string.c.sisc deleted file mode 100644 index fa9ecef6494ecc446c5787a7c542adb16e1900ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27516 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 diff --git a/lab/Untitled Project.si4project/cache/parse/lib_wait.c.sisc b/lab/Untitled Project.si4project/cache/parse/lib_wait.c.sisc deleted file mode 100644 index ba91a7db4b776772b9d78279cca836db1fa0ac40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1734 zcmeH{Jxd&65XZ;!d|^=$O^QGQ7B*H|SXhYMCkP}O5G=eTd*Gs|g)ix1p+u`hOlc7a z2Gm9a7AXQ(q_hjBN|BFXrB&Gce|tOYu7%eJ|1iw!Gc!B$JUh~EHf2^G5?RUYs?F@; ztd!6%ap>UpMHjY%ABWxWR=dalG68n~Nc2D-^ii}g(lxL^R2G4eo$Fxv%U4YHP4uwE z?Bv~bV6A(d*c@?+`uwL(g&3;)$O-)J;K$~=pUs~kK95jJRm9l>dT(s_dJ4{BA938D zd8yqWExuk@4Qm7A5{4I|SEad$XR_ye>yl31{7j@~ebnEcC$qr@;+Ut@k;v32ufL2| z5b_C@HNeW}#!&wfstT8~{n)+-o0`k5?+jr(%Q zaMyB(b5MtP&}=!xOo=$-vvQ8lWi5pvWGF7BaSL%$wkv yOrljZlQ+BH3pX9RKr2l_PU-=#&33EGTbVpf@_LMO;)vPM!x)=s#CX5-=J*F7p{G#* diff --git a/lab/Untitled Project.si4project/cache/parse/user_badsegment.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_badsegment.c.sisc deleted file mode 100644 index 5c81a0e498f30de53e0fc2efc6d0ccf6bf0b4213..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1962 zcmeH{v1$}y5XZ;M<-BY(M9y$Vg*$=>R&HToA!2Engb0?NcVZUeMS=+_Eb<5`(g+q7 zM$y7f5bHcZid;xxA$b9PgZ2OW_U3MH3C6}o9GIK=X6Bpu{xh=|SIm~o$~VMTGCS6} z_qi1KP!it#);ou7ckjb_M#aaW4#APuQ_ZrcqcQaM=uj2X{ zvwhjV?_3Br>`>aL&FeoRg@ki(=!cw;?+$)!e)hXa;!dKDjE}^Amza=udT7Hpg(FA# zoM~Dcc&J@Vy6u(q&XeXHDK}S=$ED@Fi?Wl~@lQx6uRaYM7dtto`DSI(ZOAkwD!WaM z@%3k|q>GmC>efLcy8SlmDv;qf>gw}}dX;L^mn)N0aI`0NRqjCkQV!ICq0_qhKrtUf zq}hTbwLF(g+^*j;V^Ib)+F6gv>X$cGlMYD%K|=tKWIPmdThnZ?Mscm!%0klC+S4{k zLtiE9{X|zJJt?+kF6sW%=}9-ZoF9)JHlm~c(l5!^A*sgm@a5Ss7KZK%z+)JX#K_Wf zhCB|PI)6GI;^+H!yjw2uxvsHX8e%1j zzx{7v^%Vq{4;Bfz?e|y<+tOZctzFY)mKGkiA_D^;n9mpX>#@Mc$FpIeG8f6iiuRC; jI*`}o;Xk!F+pp%rzXbL0xkM-Co?`BQ%3{1g{sF%L9uc^g diff --git a/lab/Untitled Project.si4project/cache/parse/user_breakpoint.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_breakpoint.c.sisc deleted file mode 100644 index 002219841978a58f6b1a788fb1dd515a599a0ccf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1958 zcmeH`v2GJV5QZ1WG3G+hp%9q}BwL7viWKQU5oJn)f|L?tBYaXYC?XMM6kdQxlys1g zfP`phsCfjOii#J2GTrfiyEnd#iGYR%G17Q;XJ-Goe`Y*ib8gwW>J_Q0IQLce;fIRX z16g$E%kVV5olpDI*a`Pv{*?|e`#I@35qd6tA=MhVL|T<1--pel{%W?)`J=RP!nw`Q z&7{A5*8A`yc_D|_z9mZWcOTFXn27BTaeQ{Sw^;Iylb)P+r1q8^kDHc0VrK|M5@Y81 z%qT+bnQYKqUGLp%UDx62YIe8MU%VwhxW*rn;7%4QXz>uR~X%>}kvQY`RgWTAJ3Ts9#D+D>`S>&C)6${5_iBqD1L6szA4T( zWvOLYW(nJQ!8sOXBr$HDTUMxF-FTSw0EGk%fjpM+SUd5xoExptdy$rFBtL<6*6w!! z^?j9XNCCYdHBfx52sEAjJ?I9H)fF3lHm8U2DSQxr303XA6WR5I7 zcf|5IQ|C{|Lw>t>6L-)TlHYt9%SCccj*i8ug}M{j;PE)udGXqN8Mv|%aBcIa-zvF+ zV&DF^xY`QB%La=@%=c@qV=LJ0ch)ZJF-wbQ_tWC@N@!F3Qmip>RdHGDsXtTw_4dLz>ks0_ z3Fkh3nW-Lbp7WU3(I?Gl>(}{{(NR*)iTMMt<{!G?2QK7V$rhENV-plbKO#2U*CjW83}UjX%ufh zD{{q)-Ti;%Y9kmomS#eR{W;g6X=yLER<6pJp@oZ5gk(qv=J~>AzOI6}AJe{}f=7|5 m4xHhOIFQBU9UaQw{@xY;moOcZ=%G_P(EngJ=-AtfFYp`k47{xX diff --git a/lab/Untitled Project.si4project/cache/parse/user_buggyhello2.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_buggyhello2.c.sisc deleted file mode 100644 index bb9466b0f7f49c4f8eff9233a24830bd283d52ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2060 zcmeH{zityj5XKi{JCU6rBL^b>DJK*mQQ?AuB8u_|28AADNBAV;L=lN1B;Elegea&G zK}otu&`?qE04Z=Pij;I|(;eS$Z^u5LB0@uh7-?>HW@o>-*>CO|W#^WiE8dp6f^!QK z&J8{nydKJ;+dl?V#J0cfPGirz|MIWg0Nh`Z&Zwc+(sxq$tw@Vf3H}$^M^Zaa*kja2 zG4?L7k&!HJlGKMgoUOB` z>aO&9tDxfPBlA+#(-5;)X}49oS|(*EDXQ<+?UJ3Rx^lmx^d$>-nq9fwtD4v;n080JFhflQmFcQ3mwj#@V2o z9L~~nAS!;oPw83KUPrp5*u57MRXT!n8vtGgdSp(e$MV>x&Y#jlelwi0+cL|Ztwqaq ze3NlntXde2xc@BQ>yi3838TnJ(Cb(IPToEr^$Lo;``_x-Mlee(WFi*(eXqG`X?I)e z^E%AXqNA%wo1r0eI;b6AQ}c~CGt@jw?8sOinAAh8Hh-Fzy6CO_oBnTMIcCWtXH7Xf NpVA!r?)bNv`~r+hzpnrQ diff --git a/lab/Untitled Project.si4project/cache/parse/user_cat.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_cat.c.sisc deleted file mode 100644 index f6a3355be9294dff7ad370824957cb8c1a014f8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4012 zcmeH}&udgk5XXBniNY})J4LrF&Ui&O2?2vfhHk4!wZWl}Ky9IGX> z4Q;ZmUexq`l_@%8T-6wi9F-e7)>O(l3bB)Khvb6D>K0Hi*IN5QkXcL-W7S7IIvY?Ha!ZPN~25 z+9^r!HhQgdU6NoD;igQ7FwE-sY{a@zlC;SuB~Juq+t{Vvcm!|4Dk!T8=CdkzB=b+= zaaF<0-)}rx{mzaB}(^(|W!47Ix1!uJ9v-r3Ud4hrHq}a}6ZkY|o z2b`n87b)~9Ng5XT(R%%ITsxtGU2(g{I7!kuh%vfKDl`(^FJ2QnQ}Rg}1d$QeTojVa zF+_Bg0CD_g6U3_^;`(Ya2}Qynnog(S>LN9~BH4|Z_i+S7i_Rp-?6WL<7vdzqRjP_rUp56=)Ai_#=(HdfJW zXKs`)8apjLw-k?DTN0}tjGIVfrErP0NAXXtec&Gkv4R4xWUTf;IKwIr37KyBSPKuN zf2enqvIsA>s=%&f7rRO~vqn)}k?z>uhwUVW?f?wIBER+#N@?zUKmH@92eo zK#3NBA2Yd4(m>Hw0??y3nuA`qK0$okH_~3U>7@VmKH+WuXRc1 ziO**~ZIuLhx0pnLYPy|)kJX9v?P%Fkl8~~Ld;$kAk5=NtzZ{GYk=KLqzJ25{tk|P^ zo^yp(bYB%a)R8_Ln|s??2H1NyOZy`^~r_kLlCUBm474H9SV-*?h@lZY`R=c-<^$? zZ945Bz^{9tsJ9kW{htQLhX>w$D^J4cY$CxpXD{ z?vdcx!)C(@c*g93Tg6bfURwG5!(59N_)IL2#YIyp(;dz| vr-}HuSOXy3Gp9XMXVX0fp8m7Os_}cXC!P8)4G!u`g;P=Z0%1xcgwTBf3EQm# diff --git a/lab/Untitled Project.si4project/cache/parse/user_divzero.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_divzero.c.sisc deleted file mode 100644 index 3ca2c41c89f92458f33e43e7c5fd4fb2639baafd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2059 zcmeH{zityj5XKkdIN_WGBPU7_2wNx(6)xyN2=N2~g&t!^_@v-i7D7K|ujU zibO$jLqSKu15n_If+BAKCEfA;Zg1``aR5c>5T7(RyR)<3-2Ay~l$={~u6RY_3eH_0 zcW(H;5X+u4vh{5^iEZoC_7wWG`;UL60`&g8WJU>XN?uFoZy+g3Vhn4+&v&yEtk0fu zZdUT~OD)*nJQv%%lXFpm!R_yXT;jc*X|!S(C+EbmS+cum?>YMXAX8g%U&4LkjCCcm zaW*ZF|B`xD8kEw7{!g{ z{_0&V*yx*!QPvYe_WJHpSYn^&OJ&XtQA9{j4A0owqh{D;m2X!=?V8{A?V^^*E!lc! z<|bepR7z#;utp!%E}3IwNgS_T_jG$F;h>b3B__*0zrN-i6PV&)yRx_7eT(a>VcYsc z*+?Eu&k1CURS*rVf@;e}Z}Cq! zZ68GzEg6ll`}lCKZ9U7sr>L9UA$#maoB^C@)fM$m~!MP;hK1b@~4gmABYlIJ6p}QBy+c*A02VfnNZfb z$8yQeLX$0ZS}K>P=4NmM=Bgt(n9whB zi^8Lh!w;MoHvX)3r8BZHCu`%Kb#7o24zn1pD~0oyo?ib=;#>TtP)3SWC+Qqs-6s3IuiHFpj3gJ+3Fg)2W zg`a(_c;fNmz+ian7&rT=TTq%---}b1aw!RmD%FYnhdFMbW7$oXx?(p>UE9Yx_n6IA zrj62_y#R6IjAf5e)@-aaJv7!iB}Gt(V@~>?$X=QZCL`*?+J*wbjK#YddMH zl_YjqqC)}WHMf<#hyi`ur{||CO#Q$P%dp`6IIyf??1*?12U24(lAxKf7AUdm07v?; z-N2%zuhTUJU^w|M(4%e%C&jDIepBm{6qK(MV?WTQ2m1`TXS+ImuDt}mB{9l^VINzg z$hPs{?vJB7jN(+KeYNdvV7!7?&-vf7t&EVwfML5z7Z>&Iu?~rddxg*kgqUKyf-m-u ztk_KAOqEI%x*2R*El!1UJ3@_@?Pgl4!EN2F!bv~uYlJL)XYw4#+Ivl9wvm*c^_jAGx!%-XOV^#W*HYV3!>HLy z3N^PklP-Mvae*J)yaJg|f?H7(J7w93c&8q`QhV!JI$57VY@?d?zrx&OPO`q#=!~`n zUkJw)1(FNskRDexZxfDbPJjkj5E%8x&Fi~y(0Tfkv=0ku+E9A}8LxTSq#X=9J6tkZ zg+C$=UWS@f<29F>37KEvk8+I=Y9TRRbD=me22^dS@U=$LaUp#TYEL!eJ?S+fE!Tp{ z__nY0%tDFaq16c?_dO@X!MMSA{u^lH_w2uu_*@E-w&`>cHk1)O{#6d3$ zk?r$**&~vjIud=;hX)=w%=-%04)rhTq4D(T#iIk`V=qjM4Ysx-x2{5lRSoM&g|Jo< zuBjcI>WSL{f3q+ghoTVe&oW-qI!FT%vrz`H{aaNCd;F?h(fyY`s(&46C{|De4Ka||!4M6DkT{rt{0RmOP7G38ffu2W3XP+SI=Fy9V$`7v zF*>P!vFS{Mcw%)(!WNmf->0jvp|NgZ2qzc*)KNIU3xRf|4j_5%-HT38W zBKJ<*+v42n{c>vc*m`@UuYAG^ZQm-j%fxXsJuxA>JLr+w*?fH;?0w4VFfGQnj=E|x zbjRwH^*F+?r>Z4)Pms^fujFo~N42>yAKXaHja`)-Y=)WM5EoCwt8G=k_pKE51{oBF zZR(vsiLh^5S;z$W!`k^>9amRfrK?4y>X+&Cy>jaD;M1skuYQ(NAh1r+RnE;i);tcmz);_Hk6j|B}y83?^Xmh}LMCMAwQHB4S&9Wq?Ss~)E*EIBVHmaWn%NE zwSGOm8BIR55(U45tkou;TsBXvkjegDE0-fC?33BKOo5~SW78@&y&e~fj$(II?2n_h zV!79ZvSRH=NmNHP#3zzMX7B%qSX2A@n1+O@Yoi`h2~6EJOkm+69TcXsip`q}TOHa) zKb<<^Qx�?+EwyhbPg+m&(Z0cDA*dyqV1GqnzM@3>)uDm-2p|d>zdJxD2yzGcKD; YQ?ZQ=Aoz`VHz0c%^nO&If72oR1LgKHh5!Hn diff --git a/lab/Untitled Project.si4project/cache/parse/user_evilhello.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_evilhello.c.sisc deleted file mode 100644 index c887123c752f1e08c103bbf6def21800a8910142..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1966 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 diff --git a/lab/Untitled Project.si4project/cache/parse/user_faultbadhandler.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_faultbadhandler.c.sisc deleted file mode 100644 index ae7d619f47a14959eb4c9b87765c4afdd4c4a493..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1974 zcmeH{F;7!b6vwZ%l==_?DW)1t+87rnJ2((E%#PJy;%IG+yaX#D!o<*zKq8BYg9`~d z5Ec@`#J~q&u*AWE_yt_up8wx{@9=Ibu`Vvgll*(`J@?#m?*E+A*C;u+;9T*x;0n%t z)4sn~@HmkMTVMN^v27iFoJ3!A|K(q)0KLB^%!<&a@LtduxJW1p$YH-0{kVUn%=$p6 zUU2SIw$sfSZ^MoxnQYI0kL2R_{y;y(L~M3A$7XxGi^lIf>B#s@;O>}!cs-rlIGge( z`A--#Nh)UtDvh%j_%yUwjxa>p1#;I8^O=PZKB?+%e zUr3Iz)mN>!%POpD?{h7hn@g(-Ww0UJ@}+8-Y>lg>2{N!opI23xy;!H%kOv^6s`^Ae zpS-7YkED$yCSxD2?>fhzoXKEi^H-3+xb`~k5R}JLHpruC4~fiH531Uz43}~!XlvzV zo1kPMKsD3Xr7bA7dJyz@YIM*wE{E+oR4cyROWU)cGls9Jp?|tQ?%P7sZ2))-?U5Q7 zdiIF-acE_q^nloFgEjQSk47SCxWup;y-wbnI7O!&l2WKSU+LLX+mvbeG zz5Q=-wGj*(OEVF({hsU4w6qsnE4Or+p#^yFy8Yg1 zEb+*%T87}Se57+ma(lhavTe`5O>#hKfbRo0gGFi3e7O`ME5{p2UTck}GWqb~oUt5$ zp#+FG$UyPsQlR@Mehs?9VJftI6|wK-wfH+UEq@^0mv4MEW+Y!XW15XeA%C2aynWV~ z-5?gR8*I)7&5S%)w zi;Ii1gQJ5f4h|yjc659Fex5g%ym=|s#YH^$=I*(>=kB>5Z<-~uRkPv+!3t*IbRNDf z1nh}}-7mu<=yu<~JPMz+|MahPfY~nzCsfc=;klqSut+Eh(4$ZFsJy&bMt&#MX3SQG z^{BUVJm}Cv$%qCQe?)Q#=U~teF(Ka_?CAXLcj5RQL|rLcg5Sj_#H~zp*p6YyP&Q_g zRL2%_SL1$XZKHdyab3ctwfJtKcjK1y#P#~=1##zvIl;+mdULw8;5vAc;FWrkV{YR? zJMJT8ySl9oe!h}+m6E|%>6$aOGTADNr3Es0wFh-o=0N^Z4&(vQVO_l~pI5=t?4~%m zJeG6JE?+idQN}dgS@%lw7dIcqU4l{s&7gQV;~|mj8fN1)+CQ0ZWiDuY{eFj_p|9e# zQJ^#89u!?86SP@9Jm@Buv*W4k)uZKW>3CLU>k2J>2p_iQ!&n%)F945WJYrKz&lz$* zWb*v!c<^5t-SKWY$L6xea+b5x^jJJwns+wtKhD=WP+fPfrnRyX)VeL*&nJ^wiQ;en zTebQMQkM@F4!Q34wdS^^(`&Dv*JYL#Oj?l^LqUMg7x?v-zQbqVm%>1$&Xc?4^&u8{ iAkWFeKPuj-*XP2&1o^NzN5*F$pZyo9k0;3ft^5M@`?@0l diff --git a/lab/Untitled Project.si4project/cache/parse/user_faultnostack.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_faultnostack.c.sisc deleted file mode 100644 index d5df1b4281bab9ae3b3c43590e54c012166e8fdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2303 zcmeHHJ#Q015FLzTN9P1va=;`+vV{b$CYZK#B-G-VFk82=9%d~^ur!K z+|IQ>)V;44ZRc*|ECBY=x$cvq$rhIjCEma|W?|=`#yZD6MJwT2{kfi|b2rl%Td&s5 z=xlvKghM!z@s4Svtuw#%C=6JzOZEjv@{%RXxg*}gwqL8wvS5;B42dXN+TDw=HIiLt zed$m69a(ua~sQMQq*=Z*NJKAbp4G}2wq5$eF{s4gowEm8a}`~Y<^kfJRBtIKIX%GypN*y g^3Si0oRzmH&EKy2sHPu%cH#5=x%JUE$J$hW0#=UXssI20 diff --git a/lab/Untitled Project.si4project/cache/parse/user_faultread.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_faultread.c.sisc deleted file mode 100644 index 3e03b6b6fca35339444b2816096198003b3b2e45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1958 zcmeH{v1?R85XRTL%XwZjJc;2Dl^kMYWeN?5K~h?(M6k?xC*Fg2hG5d9kyI%SSX$Vm zFh#5cu}a||kitu45v=TN?VjIn_szY{BdCpyI50Q6Gqc~kZ)Uiex^oN8)viii#ksG# z58qe39>}8GUxuggZGU=u23vLi7z>j_I>%mHU5wUcPv*KFUBcJ>f`3aW*M1*8NXwz zPdn*=Q{2@z>fmPHRVbr-pQoGi)mDRQ`n&uJJe?&%Va<;bvI+1;u*OL;1d~pY&s4=(YfS4C9eG zvh>^$%i~O)KOGPGck?%K2Yn&=&8M+kB-iBVSgcy8TTKVg%UqKK@tO@>SqZo6#o5XaZM%XwZ@PA=gh3OU5a$`qmoB=`{$iC~%YPRxUNLNIAkn9?#u%0RRT zM0=Z5Df|EyUa+tTsnY2;c>aI8Z|?0qg4)=K1M_D0zq2#@pBdgv)wxCIDz~JroZ!WPk2^T`qp_5A-1>Vza|JnCck%IY8#%_=Wey^V zW2R|yctY-G((kUU^`5mKXmNWbd0JYYdnBB=#vhWz9m`b4i*ed&RmTm(rYW9*8Q-zB z7oDWfYNV=z>H6v2P*o_So5CKJTQ#yZCadFQFt%q^RmWcJrEJI(a_1e1E8s6=pU1Kv zvh{*2xh$4hj%|I%IR<4YaojXDuaLj8{xazi6cQ8!@>tqq=E;0O`bn(5B~Gnow$NNk#NJ*mh1SMjJCzHg}mjY|EkFKvv}3+2d<0+T#to)eVub9 zioN}BakUYImo*lP81`$fg=y(7cUEueGDC~zr3hpoWW|V^-#8Natod>&N}eS;Ar3hZ h%g3V+RmdN^H~b&Me9Yp9&!+ySZ{7tT3w}Yk$xjTkyB7ce diff --git a/lab/Untitled Project.si4project/cache/parse/user_faultregs.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_faultregs.c.sisc deleted file mode 100644 index 3cd4cee91abaab1d9df29263c392232e827ae8f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7457 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 diff --git a/lab/Untitled Project.si4project/cache/parse/user_faultwritekernel.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_faultwritekernel.c.sisc deleted file mode 100644 index 5f802013e778eb6f5990bc6b77b1562e9d2eabbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1958 zcmeH{F;7%M5XaZ!KzxXSpt%@G?qX;th!hsY5NpeWK!4|*k(c0+5Fnwu((;NcG%?YJ zM6GO0jFp8SfP$m4AbbJ&2G9R*-RiX089(bLEG##w)p zR&F?VD&J{;+WYV$NhV8+zaoYBy+6U<4)y1&hBs! zNQjvvGs6>d&%~Y9)?WL0ZAFWRTk*5f=94w~!8QJn1a~x38861EK07vHK5UW@nKI)$ zy7#gfcUXm0?H*U74_Q^AjDDBz^K@mLZ0ABHgR#A=syg;!FJ(iXGtqq<-}KWZa7B0xnd~`pv|2Z zEkJ!;Wh+uZXQc*;uNHvTYuAJBb2)5JT?77VB^~;8owcP6ehA&0zAZG}27t%V9+?Y6 z&mOTj*5vuq_7Gps?t~TeiR3q*+A>G1$;Gx|vFfNM{_uLn6- zQ0(o0i>r+wysWWU#C%tCElf*mv$->`%M2}=mm(ztAuC4M{Hc>_^g1huLW+`S2~NNv j2V(hn^m`Su_vV5Bhp-s4#1Yfb@BaK<8e`zMZ25q?> diff --git a/lab/Untitled Project.si4project/cache/parse/user_forktree.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_forktree.c.sisc deleted file mode 100644 index 4b48576b6d42966f5c016c3aeaffa4304a62fdbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4847 zcmeH~&re)c6vv+}P#BYwgGsXf!=W7SiGsI(U+;r=`{%p;(8J09@L$OQwSQcAOaXl<{934% zNJ_#EA#h{8T)Okha5v+V>XB!K_r*(J{|m7OWC6xr{G-$kaROAbc@eMA&H?j`Sy z_XN?LOzC$>n|>0#!e+rQq8CM(T7OOlnmpEy3S)?EWZ6(-315clv(=f!sp<5b1~1Ii zFCD7A`nLG!bv|+$Y%nG<#Ih4)8QCZR8=Y^wIX-dWU5%J|C-i9{9y4Z7f0&CTYe&0! zp@uBW3zARK(|!5s{6`D()6?&1Vz*cw7CH?*FakJb<7rnf>xL}rQMg5AQQU*#YOOvq zh0C~gB8b{u92p5bY+2iKf2(FBi)3tanG6$iDg;gmQG)2G&|sI>M!I@YU??(NaQN+9 zyfRszMLO=|i@%mjOD`3Dd{=9K3+Kd}>p9VlwUvQFEBo+;R_m_-vAJEE}-7Z}Tq9*Rb7j?P`5$Tmw7*gvKa+N@zpy(xP8AN|MS@YfCwd zBA~<7#d-F|NxY?lKN6BiFjK-CO5(=#BfZ$BO`!m^T@o#aEOu;twTUOE^r&PzS~sC0K%r4QZM~Tw(0& zHCQ^8L{DG@6gYU_0H&;Y{LtOQ9dTR;)u$o1X?ak?FQQ|O9 zeEXa{&|RdZ24qE>s}DdJAXIQwwwpihzOYbuj@edvRIpQ6@aR~6WumMi1_J)Nvin!&mr|5mVR2oMUm z8TTD5o8!vfQeZdF6;}pp+>a|H;L~QX1W9!rtnWLqHUWu@!AY|GcGnE@=#G6#)NLCr zAHGyREtQ%dK72tx$EjVn*$0W`c0AwBr`8Xj-w>wg^Z22!E*>GnLMTWA%}b-RJ3iQY z%{Yv3A8hmCn^R#&!YkNEc166}=S5+8?2vc>+AfKfLlz%xeYYec@4Me|nX=~bzPlI) z;*MEXU>8ELyy3xB@ivY(JBCi7YuqsevRy}Vff}&0*I-HVH({fLPQ%=ksmJ^78kACr zQ6a%Y`L94A=A}{BRJOdpo1qa@$K?EnRg^M80sRbpQfQ^{(n#srmN(c|rPtUTm*+Jd z=44t8rWbfc z1r~cR;BD<>caqtOAM4sy2EU05aD3bqbMk%Q`)6|BErWtsW|@B&-xe}{!SN0;42A(0 TJ)lQf_ z(8gEqARYhRwYa{B-ovR_g71fWK zwZ1i?!RDEuLl2S+JGXvBG70B!p&xt#-yQDJ`PtvXu{#P~DVIdNg+Sm_g^u&-a8vx0 zeJ06tenIT%xZhb`?LKJEOSrlm->>v;+>ySV8%5$*pGMvjiFr+z4`(JhJCbBvV%j}o zjITaw$9<%7SKq0Fd-G{mDH-}G-IIxWjcA_-Gm~V23_q%?QU~~?I1mQ{qq=%mHm`!E z*==!R`CI2)yF6>gqU89!vqcuzSJxiLU7V6US6)2hA&~2uX89Uz91OQ|$Z31!VF#xn zf_Oc1bV1ymqHC6%*1n8(y2fMAP@>)Z-iUr)Psg*Ub5}Ga-G^rw4rJjmsi za0YINOdN0LsPSOGkiGG4Ip@x0jpZU|M>&Ti@n~t>vAF+qxYiw3@mg9dD?zObs m%DUhK|G;DMA285A=iqg^eZ=9;IWjgcWV8MzwekGO$MFl`E5M!r diff --git a/lab/Untitled Project.si4project/cache/parse/user_icode.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_icode.c.sisc deleted file mode 100644 index 1965434eb950cf5e625fc02617d7668af310be60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2381 zcmeH{J!=$U5XYa~i(ZyQf+qw~;R+iouduLCKY)dvA%b>yxi#Klyujh*8yMRY6$MkA zXbSBV1QClAegK<@g^iUK7FJ%l{(rkW`;r8&N+S*oGds`Be_oz>wtm35MdwPV#IE4n z`HFMfZwn!RmV`IIZqH!b{P=PfS#uhhO-{*O?e^L$1OG6r)hjhQZ+shtv%v-hL0Pjq z9P0R&2e(_@-a%>Esn#{YVLa7~O0_E2#=WuEdH&2JTm8q#qXKj!Q$_AIq3kt~$Eut( z)vNM-CH}1>T-Ac)3lfU1h%GnYjB_kbLfP7a!1gDqr?j@(^#LWA0vQI9>mrfa zYR)D748QLj?GDgZ|7IIdg5mjCa#se5t(FD-IT>{AvVzD}&M>0|G-FFDl{q{^LP20d ziV7<%`ypDgOk7uU4;!w-+c4d*>s-_*k9OYFJ% z=EpgCEc>L*$Ddv;#+DHYfrG9M=lLg;-*C$o`4_vrJG*+lJN6nIQLnryf?$+_j{g+$&;Ba82sH)C6T^>_<<$ok{@G zXQH?FY^-gS`0#n-cqCsU{z%km&djx@!$}Q}-ad7r&^}$49bP+MqO%x`2gQ&%$J#Mw zqAQAVLEdl;XS;Sv8?T;a4$E`tW38MUHHhP3oiQ~fJag9?QHLA$s~3K&21_T?>Rl3j zL3DLhuY2HUK(zMfTAAIJE!AW0-{!xd_zr-%4p>@c`pu7+!~+(WZ^<~{zh~*IG#{GT_c^0;b)Tj__WkMoN2CuGUcS&k HkJJ4E%IWP6 diff --git a/lab/Untitled Project.si4project/cache/parse/user_init.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_init.c.sisc deleted file mode 100644 index 6fbac5bebdb45902a5578aba6a44d4f400ba2824..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5444 zcmeI0O=w(I6vuD#(I(GU$25Ln!MAW@3wA0lgv1iHZn}`ERtgH5Y0`EElS#-Vtsp`d zer_acZHpE&QX<$@D!3DL;m%Drl`dRFMBS*Jjl|vf|GoRp%$+ww5~10M2Tsp@=iT$a z=j)y~X@0wNN1f|?NbGu?%kOsX`d2*({~!r3|9E{Ty5;Y_*oEwM|I>dZ1JwRL@vs6q zC%zlZ1Ap za+@UQSTC7vXp?SD#IOv$NfjlnzZOsGd0Y%u8|7L$-re5zvfZynLk*u2zU(enCZ-l9 zPsF2I?4K$h?XsQ#}=at!47kdgP4gwPIzaGFhB2zJ5fHs0?yI zYg`7}bV#GzYAp)GZ8X^F37kGq3oOK(iZPNLU}(A!RO&WrG=TENs`q%c3MMu?z1opA z@NjrnszX>la=&DA(5wAa+y~rSU6_7WYczME6de+SnHS$JHZsioAGN6E8FlP;gHDr6 zguZ`ax>&BTnhA4giWX1PmzBKuwng)s`l81APRK+Oqie zS~UKN&*F=EHz^)h7Hj90l5CMhqQcIysAbnI;;fNH&huun$X&$7FsPaZZ#wl3@i(bG zgGA*Ce}lUSR2cG#!dpG>L;h4}-V~2Xck#FHn~j5w6|>ve^9T8q*xGD(-YDe1Q=`u% zHLBfV`B0D~1$GrpO4Zao>KqfjN$n}XV^6Gl%a!?OBuO7vncN}i8O||X>~Fg?&Mp3) zz25Bf1m?CXh^(-_Czba~8X~$lKwP<*oopGz3W)Uc9b$5bq}AAGI_a56O=@9~>(*1^ zW0_1E5@w*lhKMeUC{>d>2Ic(BJhovM)zvZM7kw8o0=PJ_bSsNX17w^*iNqs(p^GGL z`Nd&M;G0#`Bxl)LU9*R0qxP;L_nz>zZ=zJ3%LaQ*Q(`d6!8* zpcIl%HHksm&kWPW!F|l>hHFccIzWqarzQX;dH|>x@_sQvnJx}M3-ygbPut{R{?K+v zn;fRg-t#nz+SzS#fM`e*6_i0SR;;^C4svDGF^lFv#3!t22PBi-5Yc6Qv^LSJ&gu3l zspRK2NYn*6BsN5JSwvzo>Zs3|Vr3psg45p&iMvP}nWpP_aD0*E*Z?6w=c_Jr6Cuip3^5glD@+dtuQr@?ZX+-yph%tRi%n=-Gy6}j?Pubs^ z_LqzA&mCt<<*5_#0kFWe;h5J1m`(py;!GysB2zJ)fc%EFM!;^X#=T8IyND)rWO9!< zTQ`24ao=h#R!qQbtmBGnC7pmwB4Vv6e)oZ&_V#<7c(*cEdJ^AkKA4oMsaro*tDdiI z@@d03ENo8W-lac)c}K8Ly0HtbU}+|jWx?9`W@|_|8_4HrxkgFr2d4 zfB~NI(^|uX4=w8dPwImoQiYK0b*SsM-pszu?CFNRp6*s)h8W?x?OgIkP9e)JqrafM z-8g!c7D#f-I~pb2<8%c|FZn(8hmc`mXTNT!htb}2u|Jh+&Ey-vq1X@;n-)_ J#2S|8e*ld~OV9uS diff --git a/lab/Untitled Project.si4project/cache/parse/user_initsh.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_initsh.c.sisc deleted file mode 100644 index 74f0f8d7d94223e06c0ac4f49222de096c6e73b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2892 zcmeH}yK7WY5XMhp9_uw~G?DlSxP^_CD_B?rNo^q;jfmK|CedAt8(H$OOr^090%}AI zWHqGFPC*c>6#fBeVr3B!GzdZ(+4B1R?!B`)cO%BeMjV)(bLPx^^EhX2l52Br(7Bf5 zV%O|kZ{XbO+vbQrO2SK@Ry)uweR#PWxx@XZf29N5{ULF;3VI}-7wZ`~Jp(<6gznXX zwWn`Tx%c9pCg*a}z1#aRlUPI|OlbLLH6s(p*3Q9%?Co%l&i3Z(`(W?Wur^o4_@=0H zQ^(n!)L}Z0Fzl+I61%HZ9vYn(9?AA;abmP|t+{ySy!7N|IUm*%^@>sZ6WG`;;FBED_rn&pIpt$%vAlk;4YCs08k#tqV zn(C^y5$&{vByoreZP(R{vU!D_cz{5nAChMka8YtVdyCH*c^KLHlyf|uI{7ykhV?fT z<*T(iXfEFq@h5*GG?n&ekNdrZf^*~oXZx1?o zPEN#X^_VLF)YGMgfHR{o+}x2^Qwc=##EmtVXNZX|g=meInJ%;eu>vAFfwU`;9g>Ea z=u(K}VzdSE4y|k#gUG!g_lpfN(WMZvG1_wJQ-$*Qpic4FCkBXFqA-d#-BIs;a_iL~ z4N6Kek$(GBu{1i8JqeaSRZbI>Prg;ez>7-UBnOZwlfAzs;t)SrdH*4j= zSbuExkPz`%|Gw6SfgUOrDviaisaW?c{#;%XAjRfY?8@9`u@uQH{LNx72%p@##XJxz zm=a8vddT0aLHc6JFeaMQR=S?8^yN_a@3^1Rp2X#HM!6emLI3PR1~=w|G$17J*xqHN z^nT3FRQN+?@23}^#J|TRC#+_-nppP}jVLRl diff --git a/lab/Untitled Project.si4project/cache/parse/user_ls.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_ls.c.sisc deleted file mode 100644 index 6b8a8ff225156e0faf0e729009c2f2e8ea9b23fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10217 zcmeI2OKe(Ug82yk1{SV3w#h%O3L;6#ALL)0bdBLOv@ddBgHsWb98 z<+aHM%46e21%-Ao2nC6#s-~5wKy18V!P1)|7DyOSp}f5d5)#Azcklh*Ilg0$J(lbR z;**ZQbH8)X|D5l6&%NXF4N-J9ipEZ=MWa!4?>$lU`cFoa_A7DP)!)26f$Zupeta|h zQ1pNLU$Fqbf4kaIDfFD$i)u&IN@`d(@24elnI z?8z9rWz*p1J$Add=cDZF_WI&n<8rkYKcvq6jrvog3m<+&a&)0#ANb8`U|V)r{q3mR zwN+Ey$L1TAi=WlqEa)NlaWz2c1IQvQ0ImG3Z968{-EMD5a1J+G)%wzmdY;W~+;hJg zsPv1d}Wti9{Yn4~nxY<|W-9nY+nqCQ=zFVe%zUcd9T>!tGZX%3T=wD_CXODkV}rNn9*e{GkN z+3K?!av1qm`DjcHCt=GxXr~;0RQFs)+d)cx}m^Sneer zi!tvY6VJuYT+1hw9lqzp1MJoeh}6Y{mc>Ehb>7%Tju4Aj1%Q(ozfatt$l@UJHvYH^ zP$q}&xuRH{%+5BB#YrV&{A3!7;X{$l>~}2MTIO0Zx8TsRSX6dRaTgV;Oh*Qo$*K&> zq9pSUN?Cgr4%yp7E6c>tv#}J5UPu$~N7Cras*5X<-TCs*^Cup2D3?dn@GMG)UnGqypG37DWtD=_#97UW*=-vUg<)!XcEUKeV!hT39E_D4U%|{v}ILCsw6w zyVI)^%%QnXsJmsIaQ-sR{TK<_sZLOzs1aw?h^}kNtX{3SBfB=|()~Tx3QopvQG@%G zY*`%S--cdebW2WsNAIv!0B~Fl{$4dHl=P9s0qEp<5fpo+gw?WIL1vSJcd8L-aEuJB zWyQ>9%+#0J^Hs`^q3|a#=x0b|MM#Nq>MK8sAhLVj*eE~nThu_btcbXIlfA1Xt>(FA zPp&>QipSZ)gL_(WJNsywkbWmlET?(%n)@Q_6W&o@b@mhLnBt-#^SxDa=9HovIHd(Zn2# z1npFPI0C(-`yQ7ks6H==kKz@PPb(+V%lvx^mzE7(fjC(3#GO*(6AK*cvZiHmSSv0JhTyOI=6_oWpfKqhSH84=NQ6exI6Ak;MV(~$3S)!X2P1Heywom z*id$CoXrj$`a;Lbps@OWr#-H5aKa(WhEDsUveQ!bO7>NNl&h+(^LBQjr!0yx@1T@* z%D^F%r9YIiPhGmyC@OnJicMxGEaC}eo06?%PcOc`63!&Vekx0SU~eeqOol_24OX^L zA1=+$U+8;Nh~(P%#lqJup4Ff4xfUE}(LuzqOAH)BTl#~wEtD8O2E38vO;oJA@v`%H96qdCx4osA5fBc zRu;pC3+XbUy`CQ^V2{YM*Ti`@$4k7?Kt4a5N-y(YIk>cJXd?hh`kYq77;qoBEsKMP z(#%d@1}jS!=K#er0K6^Z?+`~3%i;iZ@-J=H&Z*B`0)2*81JdZDY9u|JM>L?8Ngi50 z8Pjk=eJ>kzpBz4>@!Qnkyb6M7SrKtmW2UXP*}{Wt>l13+Y+Dd9mW5CGj&x^1Q@7jG z02_|CzF1$LTZoh05_&AageTNk0Gn7A0x%ao=YYnlY8#TZ z&!iQXU(9R*7aQgb7O<@x%RMI6v8lP&cHR5 zuc3Be?KT#U>)xYT^Bm@b=k_`EY5BhO&ojR~`a-WhYHSE$74LOk2sj91&{&8O z_6N-wGzLaEzFmLEweSbNg6uPDKrDYOJoyTtmH3P?F2Z}tdO2yIOYb3;AY;)#!5`5F zQkMMX1sQrEWs#n0`6t?YG8ybt);}M}NPUQef|$!=7viM_LPoYiBp0OQ%KN?#nNOP2 H>YM%xxoxts diff --git a/lab/Untitled Project.si4project/cache/parse/user_lsfd.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_lsfd.c.sisc deleted file mode 100644 index bb7d6ccf3340561266d9eef92d08861c17f1c908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3032 zcmeHIzfV*_5MGZT!UgmM6n`b2XpAHzUSVOOgug%!1sf9Olu(l0Thn8owm5aHV0=;t82__AI>GdGkH`l4_Un)r-DZRFveb9D(+R8U` z!5lUGdZke>v*PMCjy>k=1?sgvz8cnL>02c!Y>bBW!cbQ)u1Si7bDl$eQZw}$g&u9> zLnG}qKWz+KFDAeK)~Z|FxvdMgf;=Pj!Dbc#?6Cv;23~)FtB`#jdK;#WU>UT6Cs?}m z57Ox>3)eiSS+GpQVt9`AC=UfG5%RB*dz$}sBIuM^WHQ9;dY-9u+Tnl% zNvT#Sj_J4T=H_Rn=#;3b*&mIEFv=-qY0qi438xF^VZ@Yofq5%9@*dHtWRTarCEOEP zTU&*t3@&3!o@hodd%^NZS))hhN@T6l?%;Noi-qai^TAx{0#q1Vx5v9Nr)S9;J*!Qi zg0`xiLwTZDVov#dusD07QNc>KwabQUfI=0o!VyvK2lK?EU=>cXBVJCc-y2_&9#}Y{ zT~9Ebo-UShrIW$XiDGGR_*{mN*#|m3g3LYIJqB*0@cFp%#;W+pVn?jI$P$UH*i11h`D}%Zr~-;IDdEX-mA@iIu&h*mNH` zl8BY_t+5J`Yme5c+1$L0wY3CbBheGeJr>p=0h9P&sIUY{eSy{cPgS27tWQd{^!-Bk zddIQoi&!??r$6yA%*l#7i+|d3VTB?2q-|fO`Br<<`P106E#oB?k}&bL%HqE~d*x-= itI{D>UuOiE&P+HRy-~z0;@u_B!eVQLAMLU2<;v&n{F?X);*kd3vZYnTL$tp6DY05BrZAvcmd;y(t%~Dz7K6UkO-k@y9r@G!YHu z1)^T1Qjp$=bu60g%Ii}t^sMIpC4Nfd&%Bcx8na!*VVO0_6dOO0CK^>j+1OgG+;&}9 zh{{iE8fgOvK_hpIS;%x@`brw-itBOitT8hsK<=8uBO|Qu^3saHbYaG0AL+EV!7!Z;=wz^D|nID^HDyuA;pAFCE zAPF=Z)2h~A_ggR|?7QV?gC6tHr2=cvW4Mq=1(KLptCxSa$cS}rHYV!)3o+kBSG4FU z56)`Px~xe}Yj@sT?k_lHk7?5+0=ZYrnx+dgw^;E;%(OObYFaZ(ug>BWv^e0DAYpTg zF3dQs|GvG`3)W@smoceFl2)9WPI|k6q)p!?1O063GD#;iPnhAwdMjged5_r(bCbvD z6ny%~Q{E`0<+NYT*VYhE?VJVDG96c5tC$Gn%u`%FgEB2zQbHjgh zTQ`{04cT}uy)^jNvY-iVr@Mg_jY+`Rc~ntP9nIhzLAjWw#Lf+krMIt>;`LW?4@q7v2=Cc`q)@N;^5x^`-orbQ3CdCx@=8P z{*>$&C?`&*bFhK*uYxx5F$~?A3a4U8J|lP>$>^+~J??dgLw-E&CAJhD#N`U1`x{qo B5>@~J diff --git a/lab/Untitled Project.si4project/cache/parse/user_pingpong.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_pingpong.c.sisc deleted file mode 100644 index e9cdd976bfba7db4e39c4939845eb57ca3e09a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2739 zcmeH{yH8X>6vjs$vM#7RgNkNXG!ZJ-#KadEN-BejwX7_-8xaT*C~Pb&EH$w)F|nf1 zibe}9B+-J>1`1j3+tw%>CxfdC%l_IydZGU9Z^HICp!O zb1TnlJg-W^3m;aRz!u&<-Hohu|LI?;0KMNYZdXDN#gD}r1D6)piJ|7Nne@HBjuh*c z;?5n;<-TOn#Rn}O1BaqAy*u_jEfxRoH}t_KWV6FQnEl&XBzjv>LEwm(vv$tQ<_gB% z9&t#a%_wS1wm92XnjD>&EsSORv^YLdx>i#>e@Qs8)t5E9B(d8m)^$n9a#~yMZcIQe zqNrYHTRnu2`q`WL(j+Its^(PHwKH*5_sCR-R7X_R<-08@ybZ2*H{yXclB&{?FdO}P zYOsYQetcCV(KQNGlI0bxo{2BxMD#+E@?R7KSp`fAO0bj8F*FI`$9|&7W+#rNZf>en zz$l4WA&|5RM}}pcOGYa7sk*FHM)Nb*M=?sa7_k0WzYGhFu6_Qq(YYZR!xy3GJ@m3k zl1L3xNs>qf`#mHSMb>~gxZ3&R)K%e>krBWSnNy7r%LF!4@4Fi-RTTA2F z9@*_5pPISR)7yKvKR2u`a@KY5E_AQG3tSo&x>)`gPnfnnMNwsraNRIpEKQ7MPY`Z6 zxr^(W^x)Zea`9OZ4``aFUv>G3u^H&hfI$(ek^61+O5u@3(+Rd-~$IH zwM^EU(u4dPh4wbJsWf@3I@cxTy6;q+E4PJQr-hBYuH;J07RPqwYB!P`ZO)O9VZYC{ z>hg>h^D`U{8+%8IrOw64vPZJbdXkpjRFY+0?G}@)H46QZC`d?u+t}&`41Cj*csh=W zhnbj$gbf8TQ2^3IZdU^?$TqRHhabbFbtI{Nh%(S;9{ zne3oNbt9FJuTzX{`kHFEu@%Ptuki5fhBYw}Gi0$d5l+|t$CLPJH63yab+XdA|0ve+ ZXS5&QP5(v++bf3jRQYF%^#ML2u%FT26+!?2 diff --git a/lab/Untitled Project.si4project/cache/parse/user_pingpongs.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_pingpongs.c.sisc deleted file mode 100644 index 607c83a082e7a51fc68f5e0957f1b8be011f6fc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2844 zcmeH}ziU%b6vtm{YultPk+z6d%xiE+CL;)TXvNV>v_|j*Oe1{ zou2oy&-1n(c82^)5?+6^)sJrd#k~RK9`8T>D;1#k2gJilXia=aOn(#NE;01%V>)qh zVx*V%!4A(G7H@n?C+chaLLGW2h$)-m{ zwA_(`z%q;TTrO0mWQn#XKH*D(cTnsW(rT4`pGmTB6a?M!JrXX#>0Y{8C@+BKcFX^q zPV|mP-6{xb08{BUr}{#)o9{A7R3Ibi78$t}(lR7BD*Rv%VQUm=wm&aud0%{9PS)cT z`VC_3K(-F*9V&}9Y-Uqs1ZA#Jcsr*`18K{zyO8?2R5^^7)Y zAt`}@ggIxrR!nQ9Ow`V%2lO4~89nvO(5N97hWfg?v=e3ThfI=vqxLH{?`IY1g~ve8bjo3Wd98eUmuzHSJm@)hmb!2WKa~M diff --git a/lab/Untitled Project.si4project/cache/parse/user_primes.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_primes.c.sisc deleted file mode 100644 index 984f42606b75fb52124f109ecec095097855100c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3355 zcmeH}%WG6g6vnSfIwsvZMv`F^AKkPMbTt`Ne2j>=a^a`$jYZ)+?*R&R}s4)50t%^JUXgCxj{#B03{ zVG%q1xLRV}C`k%B?=AzUFn(+(ZqC;z%-Q6Na`D_2m$UtXdMln%&YoW@_JDTzX|==+ zj6or1dJ1ObOw*LYBy$h@p}BIlSKxnW-&OHP@g?uZ2g%_X^tTYwIQNwkBB{v zK(qWM&qT&t|ESEvgE4)do?om-GPpOqWN@%mR_qjzBEbn-g(u zuSNvV`o$!uHWqrDV1ng{QRnf|)&WVXXr&mOsu)YS-*n{wv+<-_vR6Y}WGSZ|fjR#~ zBOa+&enAskuSmX70Bi-#g%hJlHV?n|{BpqNCt?3NPqV3e?mF*8JGdze- zp-qvx^5kY`(*=RP7xzgw)m@tIO0bCs5}S~-;bXTE_k5{MblyK=p4olgv3(jdTqA}| zhU50#m}Lk%x+X^B8lX6`R!k6(R+6Tx1c|=)EdPY&K!?VT4g<;!0Q}_TGD)mAT_pg0 z{%CH{3Em9uS|=uy$Zj#9rjvd~nfu6T2kFUX80X#$L*f`H#J~{Iie*Ct((5S_1!QKOre$}X00XL$2#|LB{Wxkvxhf=GnP`5tl3z# zvz)n6SkFmfx4C;eA$6~)+hL7)rW*k*WTA$ zNe?%M##Za_fafd}E~o?o-sIViR}_%lE6Qm?we+=QFkgEvzrP($yR|&dXLEceCXmI3 u#P-SWC0AWb+a6*T?rG_fKXcygvA-3o0rJF023qArq>s(?gSXM^5T5{6|263V diff --git a/lab/Untitled Project.si4project/cache/parse/user_primespipe.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_primespipe.c.sisc deleted file mode 100644 index bdffcd6ed283c3ee610e566500b32e34cf6b659a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4635 zcmeH}O=w(I6vuDA>SSy+v=Q;6IMgoc#*E^wR*P<=m73Zp3L=e}q_nvp}|9sth z?o_uqH{;ysU1B%vTzaE(7rz+xd`S{M_x;7~=+1rn;SS`8`=9_%4ogZ7~j-RLLxGRnB_G4^}H=4=G`9^bj?s3h~+~dDFcSsVH zTgCSV9l|o!`MFg#E9ld7%#FF73_Z@+%E{TJe!XVY(DK_-eLmRJ^7UDhxfjM%vsX9D zm!J9CqfPwUDq{g;yrE`{<2-5V$#TF1JFJJURkNqH=8zVDBKep24;efvBWJ~jHGVII zxS{zI`i_0GWm2lH?!GKURGwmA(=rjx!4sZTwWrbL<%kV6ShQaN_B4asq0o#~2* zIR=#|IGK1S%{h_@4DlKyx*}u_0%UH$JK31l4#EV<_$G}F5nU0HoknSz&lrs~7HVK# zCuZ->L2?lc6I~J0j<`O_%L3+0S$Qmpw`&d9Ttve}SHvVPqrS+ScsVZ4E?q7`G@Z^= zPr+rOOMPCRT5fRQDY1FBM`QAjC#Jh2!fflp_IjC{BL}5qp_0@N9H<`Cgj>2#Oseh{ z15BDt7Xx>GYnAgPlG5A^`fUr#&C34$#EQR`{Ruq^7v9Vsg&FB<;tOgzzkK?gZ!9!K zJ5RVngOLz?{wOeqY#n3lm^;^MFs!FG?6JFSl5aLvwIM?6G zV&xmLo>8n%wewhQy~S$2vpyz#jcJfr%?A5B@~QJquTK@#xT<_wJiFF?bcmOFBRLbCr??)Pdg8trVP~^>;$D3u=AQ?T%_B1QzT}@F z@kga+XwR=nsk*wWKQwj&GM`3IFDErX35xfdgdllrFl4$)beUIwZ?UXPX<7iCZJw9| zl&Arq4>H4{sMv0mVCoRDbP4Ug_jpZ4@nXhyG$3i+4fJZGHEjE`@>@W8<)tRnLnsYbv#_E8R=^vNO#_vkNzK8K5=NP z8!gAs&bS?7(bH!0`o)by?BR%4nfC)9?pbg@84Hs+?W6W diff --git a/lab/Untitled Project.si4project/cache/parse/user_sendpage.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_sendpage.c.sisc deleted file mode 100644 index 3caaa5b1306dd0a9bf4b35c77b8907f9b86b8901..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2805 zcmeH{%}bO~6vl5lrp}m(_CX)yAW<`m$f89=nv#J*f|<1n)SPUFOk+}OSDT_gpk}bMHC#xzn-UxgqBg zS(&SHZlT_}<;PW_y^zP|UoSThn}5Er0blL@<6r3jcfVD(Q3c(V-IGZYE-6dMz~aZY zWL?juI>tMtk-ZYYl@D#nfjh}CMjQk>G0`n)o|R=ts*!)W&|DJo}iIY%M;Xvc}~U zc`9qv;=F80HllC1j3@NX#ue&~4rz;I7*@8CIoaU9EsK4xZIi8;Irb6=Bn?AG_DJ;fw|LWJIvjbYhUYrgw;n@IEi>bw-niUE`&JYCF{OB60ujQwPmE>Ba(U51G zJXcAH7Hji3GjH2dWfrcyMKdE1Zz+tYGve(WpDJF;WV4-}9UQ?|m;2O~e9#%GYe>1A ztXr}C?b;Muqgm50M?+n571d?)$g__f&6MTeK68A~PS)5Zn`e%Vr6!(M2|nMNb`~b5 zSJ%3zmsHmk)rz92^_gP%8|7L#Gwc3lt@d)L%k+o`7W;Xvt3*DQA1$(Y#l`9`B6Bn< zmK?=)DE94Exmb$i(yUm!D1}ylhWPm1i`}I~ynoErzyqJz9*r?8DJ}82{=|w`t@Q7} zDEzsVVt)RQ$DE(KfYnGJn|1!aN6{aM&7sHsoH>c^%l&AM6-o~bymi2z6wsS@KLBpC B0EGYm diff --git a/lab/Untitled Project.si4project/cache/parse/user_sh.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_sh.c.sisc deleted file mode 100644 index 4dfc58dd3064a95a80de3a49a2043a0402b67134..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19566 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 A4gdfE diff --git a/lab/Untitled Project.si4project/cache/parse/user_softint.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_softint.c.sisc deleted file mode 100644 index fbf40ef2023ddc713c7d0c09eb5f8d0bcf389617..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1958 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 diff --git a/lab/Untitled Project.si4project/cache/parse/user_testbss.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_testbss.c.sisc deleted file mode 100644 index cacc47a532c721bbc22709156aa89deccde16ba0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2299 zcmeH{ziU%b6vuDV#O8-T3RQ|fAR)RqnZZd!?BL)cZLESK+N6=@K^rC5f}4xt=-?ta zbSbEdU0j{~16(R91)Wt;aEUJ0@8`aEl7~ezxk$foa_&99&$;KNw!^tu=dwpdF5}$y zG3Pd3WCFbrht@VXb|PDQ^>i0})NSK`nE-FUU$j>ZJrq3_Wktn=i@BSh%EjK5eR{r5U@iT=Oazm1p3|8a z7B}$mfehVlQ5xyk-cR&Z)G!fs<`eBhMm(N_#4GG@Zzz>P2+GI8jC<8@$iCoZAzw1kNolHKGm?(+g44e zY7vye865+FvsENAS!EF4wck}sQuVPVXrsF@Pf#)tU^3wc#VshZN-Ajn{qUeG=j22K zq-UgXHi@7nliVgDX{-we{dkXM42m0xxTha|U60;Q{sm^GYl&)l^xoaE5;J0z;DAba zbk6A^a|dR}nLK0!!yX-*X%Rj)f8*%vtcT`v>LXo)_m(ZrI~sLv{od(q{kw diff --git a/lab/Untitled Project.si4project/cache/parse/user_testfdsharing.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_testfdsharing.c.sisc deleted file mode 100644 index a95f96c8d38f61f748f18f203004bfb1abc7eb78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2957 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 bhY@&#?12k=Ddy2_;zxuIY(iVSNOeB|*ZW-f diff --git a/lab/Untitled Project.si4project/cache/parse/user_testpipe.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_testpipe.c.sisc deleted file mode 100644 index 6a0d8c8858c4015de143ad77fc923ebac6c1e99e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3651 zcmeH|&udgy6vuDUiP2{a@dwn>f^Q*;t4>9FbtQ?Jn0XlEppD&*o5qw1 z0bRK0B1K197ZRnRU1rfm7qbvVap59=g1exb=lAp8JMZ0@!~{h*;(;^g&bjA)&(C*n zd4qF9&Xx9wU8i#=dYpT-(3$XKNx1R!Q4hMte}8R3e&jyT`%(dV|A}~q61pz_T}*!i zaY+n4yRsb2|9Kyk>+NuEhj@5;IhgIewh^6CoYCUhxs4hn$6wpyKV+DKNc`9=*;)Tg za=`nkX#KBvSj_nr>ML~Y^|oxb#uCTd^IiOJk0wVarYd7$zZRcQM5jBeUmug6m|=Y) ziTq9yY}0jV{VIxM%PhYBhJc8Tfg|ahL0H`(>TtbWNQb zi6&`_->qSFtKLYvH6(X6@dN3qH~&b`hU?9MDDZ-$TV%Le>H3-NQU8(CUbQLBha9m}h9Km*5rBR|eKE=~|z>C*YA0w~E*Qi4V@4)`=( z=v+R!;Y<^=oK}F2To@k(6b}G=oXJlm4HRAIf!43A4|<6qBY|!)EkIhNrjx@wQf(&= zkdsLngr{P}8+zQZ1u6#xf-m?udvI1Wp?>7>1akB66}pL9rsrT2vuQ zGUU*fOm3AlOmyv07Oe)eEy~)D8OpW{WtSv*QBO}-MEOfIXhqy4%an+IQ*~<**n~U$@J3Gd=~pY0gyQ&B z;erN=IjwVh+kGH?MLaLvjk^y6JaWq*Ae~<)H6odiQY$k?W#z{r{gLS7Nqv?$P*W?xi&@jaEl4aM*8bO;2$7R+=n(BwM4g3q`VY(h5zo)+p?KB3PeZr?GxaeQH3M z4I6mGR9Bqe{9VMv;DT?%_O@fP=2{6u#WZ(2t<%XYCZ{@md9R3xSh(XXCN1Ck2QVZ| zV&ehlLpms?4)n)#{yj4ITQg|=0Jt$z;#~L7)o5ZY`~r>NgIU#ksjUaI%6I4ehsndw zRbu@{zKgloEnHr0*k(z~B!7IugBe?&<`uX5^&Y9LMrO9!-deghIrk=K+G8*blkNL! z{--|F4zfBjCxhzalcBUmW`o|hS&~ilRBB_+2W4Z#n+5GnMe>I6LPBBko4#v2P0&}V NNbHzX=uj#W-AhXDg^>UN diff --git a/lab/Untitled Project.si4project/cache/parse/user_testpiperace.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_testpiperace.c.sisc deleted file mode 100644 index 9c8b126a0e0d0d54793c1f97bc2085f4c29570b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3682 zcmeH}&udjz5XX-%NfT2o3C3z^vA3XL4eDFC5RypLm77*eTuG%b@8&g^#N_d!!JQ(7 zR@?|`i(uh_h_>CRAXFD63zseeVpm<{Kd8GbuiwwTckZu9o{MHv9GExf%$fO~nK|do zebo<~n{clCOR=jscV&xnubxyQ{#z2h_s^>>=y)Brl&tCASyipAf&IXf}=_B7rPiz+RQH{YJ z7hAjNgsVvG2KXt`^McVL7U7RjpD%uN_tJUzttzIJc}yyDx4< z)$@LM(N6^IjHIggzO6!Ar8i<9i?UNyPs{dq@oR0cs*Qe;<8ko`%{jWe(w!3jN&uo$ zVvC1)qGp(m55;LlI)3u;hM{F2$Jo6%ANYU*M9|a`$q}-V={)DstJc0=E_VsE*808< zD9-SXM1Cr1py<3j=(WzEi(ko!y>wD3Z`K&loGy)2>SzrKg&~$P5!j|Ci01iWGUfmc zG0_ze^F?2S*g7e1_4EjpDuO;JHpE0%KqMBUFwAC*x|)Gcl)YlGcs@w3vSB8=budeL z2SkZENFOIsZ74-dbOpp>-lZtd)h>Q5e@=t!i56+6V><0uz|2rMooom6mBkv6TuIJG5c6nomv+5)X;paV;~`L7^@9Y-jwn&tf^rzcfOqK_|?_^fxB_8v`KPp zD%ZogT-gi1jm*_rE_+!cLqeuoKUX`w*uJ|yU2Bz-y{vE1c#`a-fJxS=06U*%2TPJb zZDg`%g->j|#k7WfVv^)~nl68lS9IZu2TQAq3BSR{e9H^Qsw2Uu8pKF2JY3Gat<0B` z%p3B!Ek22EQ*As3A&8c9a zK0CiKdQP8>Z7N!WsJ9Ux7U)1M93QOR$#^)YR diff --git a/lab/Untitled Project.si4project/cache/parse/user_testpiperace2.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_testpiperace2.c.sisc deleted file mode 100644 index 2567df43c73afbdb0b1312846fa1557a3d79a5fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3434 zcmeH}yK7WI6voFqd|Y1U4tW3sFdtR!6&1fQiv zG(=P^R8+(&g=Gps6bp;|2`QA6*Y9`lJ@-ylF6xpSXhH^ux}5}x?_X$888_fJ+Lo85oUF!8HezCIh&9wk*hC9^^Cq$)iy{vg2H$epQxC>SHD zD&GBUG>u7@HY0rC6`HTACuH+f{9R7Q#oy%UzW9)s)*Zz;Qq0hBO|oX=Jd zB679Z5YaUvTI;m*bBy5;X_ur-Y>YtxkukbP#JMr%Gdwl*SGy&s61*vuTP1-s-R~ju zkQp_f3#-Q^X@(^W9*)<{)n&~o-<}HcgG1sZlMt9o!u~?NR4Vpq%(3hggV!ZyUGZf) z`O0voM;me1%Q7MMtru=Bto4N#dIyIu^i_A%P8LsfY%3K>-p+q)uBY|+MDj3@Tvgny z$KvtRkMV=Rb7Pfd9}ffj{0qk%GCRijArNT3hXEU!#U8thCRwwwEUkjQeT@T2^ycK^G|kVWGa~3SngIr>|4`6 zTrY&B;@+@pcTwM+J6ZFO^o&~CogQh{$o!Ucoi9EHAaEy%2tv|kJEF1D`mINp76nx?G()@#<#2I{_~Ov zWG2aM{_n5sf2ErrY2hsXIv#39Jt3Zvpci%{<7vJx(Pw1MQ zD{pY_p7%X0B8aR4Ve4aNyJgvOJ z8o$&^KZPVQ$>4L-*<+`4ndiW^3f(1&xyEbOI)>QIB;i&7uV-lNa<8_e4MZn&uMj!o z{aPot0-!S`od?fpfspwJ-6}*WYtA+b4SH^s(=~62HKqjcj`a@|2Mbp(jg92551$qv zB~};~fgR$l4>JC13VWu?z7XvwNt$KAX1_&w^Y~D{IE=}Z*B;MzPUEd=F7XIZi)LxM zoUVCpCI`+klM##iBYSfx?JE6oMrQ@#qp zWW-z4q`*gf_0iXQHGQ>aiA#{O1B~};UmHrWe;_}KaGcopniQEQVN!5LVrRs=|Gefb zPU5&ZYhzBVIv|iS5G26v$B76h;oF#ilHPBB6l}5tjEHymRc%yLw#eD^Ler1*TTyb( z{gv9peM`U|HUgvZS_$a%hm}uk!w*L_N{|4}7-TWdaKF}IN(kgg$l`nQwh}P0mE4W* z{8c%8vB;Re6wqBwjn93ryf1c#IK+3d>b8ON;STWvh=+j<$lb;eW!eGT196&q?mJkR BWY7Qr diff --git a/lab/Untitled Project.si4project/cache/parse/user_testpteshare.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_testpteshare.c.sisc deleted file mode 100644 index 3033f9f95e3892dfb06340de9194ed03dd227dd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3500 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 Xg?0qtPih{*&;vnu-LAMj5KF{gQfWov diff --git a/lab/Untitled Project.si4project/cache/parse/user_writemotd.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_writemotd.c.sisc deleted file mode 100644 index 96f3ea75ba911b3aa48430a77b38b991bb362177..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2797 zcmeH}zfV*_5XYC}P@|RbgjIHj!{3{jU?)OXgDxrtc$5K557f7>GWaZvw z@O9|}Cihv|+v42%+-9)R@t_@>NtnpW_+qmU1>F z-3=RiU8&79k1*`1?-IMGQXQR`E|zkqH90X+8OfB-UX~qh#`&n`;2x10Urp=#EAy*S zZLlF>*rxjRMRWYyrf(K1RW(+ss&0NW$Ul#(+9kIsX--uwFFgy0wtjatfB|zxs!F-H zs*oR^IL30js$P}vYv~IG`6<1iKz3zUw0^4fCG8srP3u|v^OMf;fa>CN3`2WIF|X{* zWTglwNkJ6yj9eRm%$9Sm{=DwYHJ7*vv{1V-3Mhxs-ef$W(Lk}~JW#i=J?PA!4iT%( zQ)mI|+3HMXj`kRlFoaGl5xLq}5llNv_RKq_hMCw>m{iN;H_X`>Q{Qsut5wu&ij%@@ z)Nl))BhzCSg||CVI5Gzdp3IXRQkZF;c*a8REmaV4=d_JHSVP3t%(78GST-Tn#&rS& zyXZrj!vSudtNPY+Sa*%6Ywta6Jxq`yEoy^s~NW`)}XGF~Q=Unaf zV*l)DxlrS~QjVW{+eSmF)SCjburZ+q7iuqGU=51@Xm7ZT|j6_hHEdmp`D^PxF>pJDlw Vug5;tIMl&}-bZrLU+4HP`~rAj5(5AL diff --git a/lab/Untitled Project.si4project/cache/parse/user_yield.c.sisc b/lab/Untitled Project.si4project/cache/parse/user_yield.c.sisc deleted file mode 100644 index 38c40dcfaabf1a5a2742dcd7a124b350f0952474..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2340 zcmeH|y-$=u5XP6|5ORn>(16k8F2q7Z;T0MS(3;AK!OD+-cj~HScqA@)#Gugon}ZRY-0A>I}CV5iVSw3F+jy+mM*q^rod8f^pV z5obi4y>|7AY+s3|6vWy-r9CmNdXs`O?2K~^Oh$1|-nUMWM}BcWsp6F6tqDl3i9m*x zoXf^(a=X2yZBAz!cPcm~0uF|o@|dJK1uI2Pw?6;f>EcBlB38E;$4J_XQ^SO3nFtJDjsd>g~H;K;H8l>aw zjp7`g!MiB`a_UBref|STN zU#ZPD*v1#@^qV`LUB{eQSe-@}jzZl}zwP4PQC#34Rpxlngmy?F98edFYUj$XtzZ)7w1I<>I? O= (void*)(DISKMAP + DISKSIZE)) panic("flush_block of bad va %08x", addr); + int r; // LAB 5: Your code here. addr = (void *)ROUNDDOWN(addr, PGSIZE); diff --git a/lab/fs/fs.c b/lab/fs/fs.c index 0b9b76e..ac4580c 100644 --- a/lab/fs/fs.c +++ b/lab/fs/fs.c @@ -41,7 +41,9 @@ void free_block(uint32_t blockno) { // Blockno zero is the null pointer of block numbers. + // 0 块启动块 + if (blockno == 0) panic("attempt to free zero block"); bitmap[blockno/32] |= 1<<(blockno%32); @@ -72,6 +74,7 @@ alloc_block(void) } } // panic("alloc_block not implemented"); + return -E_NO_DISK; } @@ -175,6 +178,7 @@ file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool all *ppdiskbno = (uint32_t *)diskaddr(f->f_indirect) + filebno; return 0; // panic("file_block_walk not implemented"); + } // Set *blk to the address in memory where the filebno'th @@ -188,6 +192,7 @@ file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool all int file_get_block(struct File *f, uint32_t filebno, char **blk) { + // LAB 5: Your code here. uint32_t *pdiskbno; int r; @@ -207,6 +212,7 @@ file_get_block(struct File *f, uint32_t filebno, char **blk) *blk = diskaddr(*pdiskbno); return 0; //panic("file_get_block not implemented"); + } // Try to find a file named "name" in dir. If so, set *file to it. @@ -224,7 +230,9 @@ dir_lookup(struct File *dir, const char *name, struct File **file) // Search dir for name. // We maintain the invariant that the size of a directory-file // is always a multiple of the file system's block size. + // 目录size 必须为 文件系统块size的倍数。 + assert((dir->f_size % BLKSIZE) == 0); nblock = dir->f_size / BLKSIZE; for (i = 0; i < nblock; i++) { @@ -232,7 +240,9 @@ dir_lookup(struct File *dir, const char *name, struct File **file) return r; f = (struct File*) blk; for (j = 0; j < BLKFILES; j++) + // 不会出现子目录与文件同名吗? + if (strcmp(f[j].f_name, name) == 0) { *file = &f[j]; return 0; @@ -263,7 +273,9 @@ dir_alloc_file(struct File *dir, struct File **file) return 0; } } + // 目录里没有空项,增添一个块 + dir->f_size += BLKSIZE; if ((r = file_get_block(dir, i, &blk)) < 0) return r; diff --git a/lab/fs/index.html b/lab/fs/index.html new file mode 100644 index 0000000..0852c99 --- /dev/null +++ b/lab/fs/index.html @@ -0,0 +1,11 @@ + + + jhttpd on JOS + + +

+

This file came from JOS.

+ Cheesy web page! +
+ + diff --git a/lab/fs/serv.c b/lab/fs/serv.c index 0d7e75f..3ec26b3 100644 --- a/lab/fs/serv.c +++ b/lab/fs/serv.c @@ -209,11 +209,14 @@ serve_read(envid_t envid, union Fsipc *ipc) { struct Fsreq_read *req = &ipc->read; struct Fsret_read *ret = &ipc->readRet; + int r; + if (debug) cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n); // Lab 5: Your code here: + struct OpenFile *of; if ( (r = openfile_lookup(envid, req->req_fileid, &of) )< 0) return r; @@ -224,6 +227,7 @@ serve_read(envid_t envid, union Fsipc *ipc) // then update the seek position. of->o_fd->fd_offset += r; return r; + } @@ -236,6 +240,7 @@ serve_write(envid_t envid, struct Fsreq_write *req) { if (debug) cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n); + int r; struct OpenFile *of; int reqn; @@ -250,6 +255,7 @@ serve_write(envid_t envid, struct Fsreq_write *req) return r; // LAB 5: Your code here. // panic("serve_write not implemented"); + } // Stat ipc->stat.req_fileid. Return the file's struct Stat to the @@ -359,7 +365,6 @@ umain(int argc, char **argv) serve_init(); fs_init(); - fs_test(); serve(); } diff --git a/lab/grade-lab6 b/lab/grade-lab6 new file mode 100644 index 0000000..993704c --- /dev/null +++ b/lab/grade-lab6 @@ -0,0 +1,253 @@ +#!/usr/bin/env python + +import os, re, threading, socket, time, shutil, struct, difflib +try: + from urllib2 import urlopen, HTTPError +except ImportError: + # Python 3 + from urllib.request import urlopen + from urllib.error import HTTPError +from gradelib import * + +r = Runner(save("jos.out"), + stop_breakpoint("readline")) + +def match_packet_seq(got, expect): + got, expect = list(got), list(expect) + s = difflib.SequenceMatcher(None, got, [d for n, d in expect]) + msgs, bad = [], False + for tag, i1, i2, j1, j2 in s.get_opcodes(): + wrong = [] + if tag in ("delete", "replace"): + wrong.append([("unexpected packet:", pkt) for pkt in got[i1:i2]]) + if tag in ("insert", "replace"): + wrong.append([("missing %s:" % n, pkt) for n, pkt in expect[j1:j2]]) + for seq in wrong: + bad = True + for msg, pkt in seq[:3]: + msgs.append(color("red", msg) + "\n" + hexdump(pkt)) + if seq[3:]: + msgs.append("... plus %d more" % len(seq[3:])) + + if tag == "equal": + msgs.append(color("green", "got ") + expect[j1][0]) + if j1 + 1 < j2: + msgs[-1] += " through " + expect[j2 - 1][0] + assert not bad, "\n".join(msgs) + +def save_pcap_on_fail(): + def save_pcap(fail): + save_path = "qemu.pcap." + get_current_test().__name__[5:] + if fail and os.path.exists("qemu.pcap"): + shutil.copyfile("qemu.pcap", save_path) + print(" Packet capture saved to %s" % save_path) + elif not fail and os.path.exists(save_path): + os.unlink(save_path) + print(" (Old %s failed packet capture deleted)" % save_path) + get_current_test().on_finish.append(save_pcap) + +def read_pcap(): + f = open("qemu.pcap", "rb") + s = struct.Struct("=IHHiIII") + hdr = s.unpack(f.read(s.size)) + assert_equal(hdr[0], 0xa1b2c3d4, "Bad pcap magic number") + assert_equal((hdr[1], hdr[2]), (2, 4), "Bad pcap version number") + s = struct.Struct("=iiII") + while True: + hdr_data = f.read(s.size) + if not len(hdr_data): + return + hdr = s.unpack(hdr_data) + yield bytes(f.read(hdr[2])) + +def ascii_to_bytes(s): + if bytes == str: + # Python 2 + return s + else: + # Python 3 + return bytes(s, "ascii") + +def hexdump(data): + data = bytearray(data) + buf = [] + for i in range(0, len(data), 16): + chunk = data[i:i+16] + hd = "".join("%02x" % b for b in chunk) + hd = " ".join(hd[j:j+4] for j in range(0, len(hd), 4)) + ad = "".join(chr(c) if chr(c).isalnum() else "." for c in chunk) + buf.append("%04x %-40s %s" % (i, hd, ad)) + return "\n".join(buf) + +echo_port = QEMU.get_gdb_port() + 1 +http_port = QEMU.get_gdb_port() + 2 + +socket.setdefaulttimeout(5) + +# +# Basic tests +# + +@test(5) +def test_testtime(): + r.user_test("testtime", make_args=["INIT_CFLAGS=-DTEST_NO_NS"]) + r.match(r'starting count down: 5 4 3 2 1 0 ') + +@test(5) +def test_pci_attach(): + r.user_test("hello", make_args=["INIT_CFLAGS=-DTEST_NO_NS"]) + r.match(r'PCI function 00:03.0 \(8086:100e\) enabled') + +# +# testoutput +# + +def test_testoutput_helper(count): + save_pcap_on_fail() + + maybe_unlink("qemu.pcap") + r.user_test("net_testoutput", + make_args=["NET_CFLAGS=-DTESTOUTPUT_COUNT=%d" % count]) + + # Check the packet capture + got = list(read_pcap()) + expect = [("packet %d/%d" % (i+1, count), ascii_to_bytes("Packet %02d" % i)) + for i in range(count)] + match_packet_seq(got, expect) + +@test(15, "testoutput [5 packets]") +def test_testoutput_5(): + test_testoutput_helper(5) + +@test(10, "testoutput [100 packets]") +def test_testoutput_100(): + test_testoutput_helper(100) + +end_part("A") + +# +# testinput +# + +def test_testinput_helper(count): + save_pcap_on_fail() + maybe_unlink("qemu.pcap") + + def send_packets(): + # Send 'count' UDP packets + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect(("127.0.0.1", echo_port)) + for i in range(count): + sock.send(ascii_to_bytes("Packet %03d" % i)) + send_thread = threading.Thread(target=send_packets) + + # When we see the final packet, we can stop QEMU + digits = "%03d" % (count-1) + final = "input: 0030 203%s 3%s3%s" % tuple("%03d" % (count-1)) + + r.user_test("net_testinput", + call_on_line("Waiting for packets", + lambda _: send_thread.start()), + stop_on_line(final)) + if send_thread.isAlive(): + send_thread.join() + + # Parse testinput hexdumps for received packets + got = [] + for off, dump in re.findall("input: ([0-9a-f]{4}) ([0-9a-f ]*)\n", + r.qemu.output): + if off == "0000" or not got: + got.append(bytearray()) + dump = dump.replace(" ", "") + for i in range(0, len(dump), 2): + got[-1].append(int(dump[i:i+2], 16)) + got = map(bytes, got) + + # Get the packets QEMU actually received, since there's some + # header information we can't easily guess. + pcap = list(read_pcap()) + if len(pcap) != count + 2: + raise RuntimeError("pcap contains only %d packets" % len(pcap)) + # Strip transmitted ARP request + assert pcap.pop(0)[:6] == b"\xff" * 6, "First packet is not ARP request" + # The E1000 pads packets out to a 60 byte frame + pcap = [pkt.ljust(60, b"\0") for pkt in pcap] + + names = ["ARP reply"] + ["packet %d/%d" % (i+1, count) + for i in range(count)] + match_packet_seq(got, zip(names, pcap)) + +@test(15, "testinput [5 packets]") +def test_testinput_5(): + test_testinput_helper(5) + +@test(10, "testinput [100 packets]") +def test_testinput_100(): + test_testinput_helper(100) + +# +# Servers +# + +@test(15, "tcp echo server [echosrv]") +def test_echosrv(): + def ready(line): + expect = ascii_to_bytes("%s: network server works" % time.time()) + got = bytearray() + sock = socket.socket() + try: + sock.settimeout(5) + sock.connect(("127.0.0.1", echo_port)) + sock.sendall(expect) + while got != expect: + data = sock.recv(4096) + if not data: + break + got += data + except socket.error as e: + got += "[Socket error: %s]" % e + finally: + sock.close() + assert_equal(got, expect) + raise TerminateTest + + save_pcap_on_fail() + r.user_test("echosrv", call_on_line("bound", ready)) + r.match("bound", no=[".*panic"]) + +@test(0, "web server [httpd]") +def test_httpd(): + pass + +def mk_test_httpd(url, expect_code, expect_data): + fullurl = "http://localhost:%d%s" % (http_port, url) + def test_httpd_test(): + def ready(line): + try: + # This uses the default socket timeout (5 seconds) + res = urlopen(fullurl) + got = "(Status 200)\n" + \ + res.read().decode('utf-8') + except HTTPError as e: + got = "(Status %d)" % e.code + except IOError as e: + got = "(Error: %s)" % e + expect = "(Status %d)" % expect_code + if expect_data: + expect += "\n" + expect_data + assert_equal(got, expect) + raise TerminateTest + save_pcap_on_fail() + r.user_test("httpd", + call_on_line('Waiting for http connections', ready)) + r.match('Waiting for http connections', + no=[".*panic"]) + test_httpd_test.__name__ += url.replace("/", "-") + return test(10, fullurl, parent=test_httpd)(test_httpd_test) +mk_test_httpd("/", 404, "") +mk_test_httpd("/index.html", 200, open("fs/index.html").read()) +mk_test_httpd("/random_file.txt", 404, "") + +end_part("B") + +run_tests() diff --git a/lab/inc/env.h b/lab/inc/env.h index 465211c..3ca6232 100644 --- a/lab/inc/env.h +++ b/lab/inc/env.h @@ -42,6 +42,7 @@ enum { enum EnvType { ENV_TYPE_USER = 0, ENV_TYPE_FS, // File system server + ENV_TYPE_NS, // Network server }; struct Env { diff --git a/lab/inc/fd.h b/lab/inc/fd.h index 444b583..663615d 100644 --- a/lab/inc/fd.h +++ b/lab/inc/fd.h @@ -27,6 +27,10 @@ struct FdFile { int id; }; +struct FdSock { + int sockid; +}; + struct Fd { int fd_dev_id; off_t fd_offset; @@ -34,6 +38,8 @@ struct Fd { union { // File server files struct FdFile fd_file; + // Network sockets + struct FdSock fd_sock; }; }; @@ -52,6 +58,7 @@ int fd_lookup(int fdnum, struct Fd **fd_store); int dev_lookup(int devid, struct Dev **dev_store); extern struct Dev devfile; +extern struct Dev devsock; extern struct Dev devcons; extern struct Dev devpipe; diff --git a/lab/inc/lib.h b/lab/inc/lib.h index 88f7102..1909516 100644 --- a/lab/inc/lib.h +++ b/lab/inc/lib.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #define USED(x) (void)(x) @@ -57,6 +59,9 @@ int sys_page_map(envid_t src_env, void *src_pg, int sys_page_unmap(envid_t env, void *pg); int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm); int sys_ipc_recv(void *rcv_pg); +unsigned int sys_time_msec(void); +int sys_pkt_try_send(void * buf, size_t len); +int sys_pkt_try_receive(void *rev_buf, size_t *len); // This must be inlined. Exercise for reader: why? static inline envid_t __attribute__((always_inline)) @@ -99,6 +104,24 @@ int sync(void); // pageref.c int pageref(void *addr); +// sockets.c +int accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int bind(int s, struct sockaddr *name, socklen_t namelen); +int shutdown(int s, int how); +int connect(int s, const struct sockaddr *name, socklen_t namelen); +int listen(int s, int backlog); +int socket(int domain, int type, int protocol); + +// nsipc.c +int nsipc_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int nsipc_bind(int s, struct sockaddr *name, socklen_t namelen); +int nsipc_shutdown(int s, int how); +int nsipc_close(int s); +int nsipc_connect(int s, const struct sockaddr *name, socklen_t namelen); +int nsipc_listen(int s, int backlog); +int nsipc_recv(int s, void *mem, int len, unsigned int flags); +int nsipc_send(int s, const void *buf, int size, unsigned int flags); +int nsipc_socket(int domain, int type, int protocol); // spawn.c envid_t spawn(const char *program, const char **argv); diff --git a/lab/inc/malloc.h b/lab/inc/malloc.h new file mode 100644 index 0000000..bef7267 --- /dev/null +++ b/lab/inc/malloc.h @@ -0,0 +1,7 @@ +#ifndef JOS_INC_MALLOC_H +#define JOS_INC_MALLOC_H 1 + +void *malloc(size_t size); +void free(void *addr); + +#endif diff --git a/lab/inc/memlayout.h b/lab/inc/memlayout.h index 9b4f3c4..31b6d9a 100644 --- a/lab/inc/memlayout.h +++ b/lab/inc/memlayout.h @@ -42,7 +42,7 @@ * | Invalid Memory (*) | --/-- KSTKGAP | * +------------------------------+ | * : . : | - * : . : | + * 注意PTSIZE 与 PGSIZE: . : | * MMIOLIM ------> +------------------------------+ 0xefc00000 --+ * | Memory-mapped I/O | RW/-- PTSIZE * ULIM, MMIOBASE --> +------------------------------+ 0xef800000 diff --git a/lab/inc/ns.h b/lab/inc/ns.h new file mode 100644 index 0000000..27d760b --- /dev/null +++ b/lab/inc/ns.h @@ -0,0 +1,106 @@ +// See COPYRIGHT for copyright information. + +#ifndef JOS_INC_NS_H +#define JOS_INC_NS_H + +#include +#include +#include + +struct jif_pkt { + int jp_len; + char jp_data[0]; +}; + +// Definitions for requests from clients to network server +enum { + // The following messages pass a page containing an Nsipc. + // Accept returns a Nsret_accept on the request page. + NSREQ_ACCEPT = 1, + NSREQ_BIND, + NSREQ_SHUTDOWN, + NSREQ_CLOSE, + NSREQ_CONNECT, + NSREQ_LISTEN, + // Recv returns a Nsret_recv on the request page. + NSREQ_RECV, + NSREQ_SEND, + NSREQ_SOCKET, + + // The following two messages pass a page containing a struct jif_pkt + NSREQ_INPUT, + // NSREQ_OUTPUT, unlike all other messages, is sent *from* the + // network server, to the output environment + NSREQ_OUTPUT, + + // The following message passes no page + NSREQ_TIMER, +}; + +union Nsipc { + struct Nsreq_accept { + int req_s; + socklen_t req_addrlen; + } accept; + + struct Nsret_accept { + struct sockaddr ret_addr; + socklen_t ret_addrlen; + } acceptRet; + + struct Nsreq_bind { + int req_s; + struct sockaddr req_name; + socklen_t req_namelen; + } bind; + + struct Nsreq_shutdown { + int req_s; + int req_how; + } shutdown; + + struct Nsreq_close { + int req_s; + } close; + + struct Nsreq_connect { + int req_s; + struct sockaddr req_name; + socklen_t req_namelen; + } connect; + + struct Nsreq_listen { + int req_s; + int req_backlog; + } listen; + + struct Nsreq_recv { + int req_s; + int req_len; + unsigned int req_flags; + } recv; + + struct Nsret_recv { + char ret_buf[0]; + } recvRet; + + struct Nsreq_send { + int req_s; + int req_size; + unsigned int req_flags; + char req_buf[0]; + } send; + + struct Nsreq_socket { + int req_domain; + int req_type; + int req_protocol; + } socket; + + struct jif_pkt pkt; + + // Ensure Nsipc is one page + char _pad[PGSIZE]; +}; + +#endif // !JOS_INC_NS_H diff --git a/lab/inc/syscall.h b/lab/inc/syscall.h index 20c6433..6687c7b 100644 --- a/lab/inc/syscall.h +++ b/lab/inc/syscall.h @@ -17,7 +17,10 @@ enum { SYS_yield, SYS_ipc_try_send, SYS_ipc_recv, - NSYSCALLS + SYS_time_msec, + SYS_pkt_try_send, + SYS_pkt_try_recv, + NSYSCALLS, }; #endif /* !JOS_INC_SYSCALL_H */ diff --git a/lab/kern/Makefrag b/lab/kern/Makefrag index b6974b2..f8db852 100644 --- a/lab/kern/Makefrag +++ b/lab/kern/Makefrag @@ -38,6 +38,12 @@ KERN_SRCFILES += kern/mpentry.S \ kern/lapic.c \ kern/spinlock.c +# Source files for LAB6 +KERN_SRCFILES += kern/e100.c \ + kern/e1000.c \ + kern/pci.c \ + kern/time.c + # Only build files if they exist. KERN_SRCFILES := $(wildcard $(KERN_SRCFILES)) @@ -84,6 +90,15 @@ KERN_BINFILES += user/faultio\ user/icode \ fs/fs +# Binary files for LAB6 +KERN_BINFILES += user/testtime \ + user/httpd \ + user/echosrv \ + user/echotest \ + net/testoutput \ + net/testinput \ + net/ns + # Binary files for LAB5 KERN_BINFILES += user/testpteshare \ user/testfdsharing \ diff --git a/lab/kern/e1000.c b/lab/kern/e1000.c new file mode 100644 index 0000000..5b35d4f --- /dev/null +++ b/lab/kern/e1000.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include + + +// LAB 6: Your driver code here +#define E1000_LOCATE(offset) (offset >> 2) +volatile uint32_t *e1000; + +uint32_t E1000_MAC[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + +/* 为描述符列表分配静态内存 */ +struct E1000TxDesc tx_desc_list[TX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char tx_pbuf[TX_DESC_SIZE][TX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +struct E1000RxDesc rx_desc_list[RX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char rx_pbuf[RX_DESC_SIZE][RX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +void +e1000_transmit_init() +{ + size_t i; + memset(tx_desc_list, 0 , sizeof(struct E1000TxDesc) * TX_DESC_SIZE); + for (i = 0; i < TX_DESC_SIZE; i++) { + tx_desc_list[i].buffer_addr = PADDR(tx_pbuf[i]); + tx_desc_list[i].status = E1000_TXD_STAT_DD; + tx_desc_list[i].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; + + } + + e1000[E1000_LOCATE(E1000_TDBAL)] = PADDR(tx_desc_list); + e1000[E1000_LOCATE(E1000_TDBAH)] = 0; + e1000[E1000_LOCATE(E1000_TDLEN)] = sizeof(struct E1000TxDesc) * TX_DESC_SIZE; + // ensure that TDH and TDT are 0 index not offset + e1000[E1000_LOCATE(E1000_TDH)] = 0; + e1000[E1000_LOCATE(E1000_TDT)] = 0; + + // Initialize the Transmit Control Register (TCTL) + e1000[E1000_LOCATE(E1000_TCTL)] = E1000_TCTL_EN | + E1000_TCTL_PSP | + (E1000_TCTL_CT & (0x10 << 4)) | + (E1000_TCTL_COLD & (0x40 << 12)); + + // 10 8 6 + // 10 8 12 + e1000[E1000_LOCATE(E1000_TIPG)] = 10 | (8 << 10) | (12 << 20); +} + +int +e1000_transmit(void *addr, size_t len) +{ + size_t tdt = e1000[E1000_LOCATE(E1000_TDT)]; + struct E1000TxDesc *tail_desc = &tx_desc_list[tdt]; + + + if ( !(tail_desc->status & E1000_TXD_STAT_DD )) { + // Status is not DD + return -1; + } + memmove(tx_pbuf[tdt], addr, len); + + tail_desc->length = (uint16_t )len; + // clear DD + tail_desc->status &= (~E1000_TXD_STAT_DD); + + e1000[E1000_LOCATE(E1000_TDT)] = (tdt+1) % TX_DESC_SIZE; + + // cprintf("transmit a packet: %s", addr); + return 0; + +} + +void +e1000_set_mac_addr(uint32_t mac[]) +{ + uint32_t low = 0, high = 0; + int i; + + for (i = 0; i < 4; i++) { + low |= mac[i] << (8 * i); + } + + for (i = 4; i < 6; i++) { + high |= mac[i] << (8 * i); + } + + e1000[E1000_LOCATE(E1000_RA)] = low; + e1000[E1000_LOCATE(E1000_RA) + 1] = high | E1000_RAH_AV; +} + +void +e1000_receive_init() +{ + size_t i; + memset(rx_desc_list, 0 , sizeof(struct E1000RxDesc) * RX_DESC_SIZE); + for (i = 0; i < RX_DESC_SIZE; i++) { + rx_desc_list[i].buffer_addr = PADDR(rx_pbuf[i]); + } + + // cprintf("mac addr %x:%x", e1000[E1000_LOCATE(E1000_RA)], e1000[E1000_LOCATE(E1000_RA) + 1] ); + e1000[E1000_LOCATE(E1000_ICS)] = 0; + e1000[E1000_LOCATE(E1000_IMS)] = 0; + e1000[E1000_LOCATE(E1000_RDBAL)] = PADDR(rx_desc_list); + e1000[E1000_LOCATE(E1000_RDBAH)] = 0; + + e1000[E1000_LOCATE(E1000_RDLEN)] = sizeof(struct E1000RxDesc) * RX_DESC_SIZE; + e1000[E1000_LOCATE(E1000_RDT)] = RX_DESC_SIZE - 1; + // 写了两遍 RDH,查了好久的BUG。 + e1000[E1000_LOCATE(E1000_RDH)] = 0; + + e1000[E1000_LOCATE(E1000_RCTL)] = E1000_RCTL_EN | E1000_RCTL_SECRC | E1000_RCTL_BAM | E1000_RCTL_SZ_2048; + + e1000_set_mac_addr(E1000_MAC); +} + +int e1000_receive(void *buf, size_t *len) +{ + static size_t next = 0; + size_t tail = e1000[E1000_LOCATE(E1000_RDT)]; + if ( !(rx_desc_list[next].status & E1000_RXD_STAT_DD) ) { + // cprintf("no packet\n"); + return -1; + } + *len = rx_desc_list[next].length; + memcpy(buf, rx_pbuf[next], *len); + + rx_desc_list[next].status &= ~E1000_RXD_STAT_DD; + next = (next + 1) % RX_DESC_SIZE; + e1000[E1000_LOCATE(E1000_RDT)] = (tail + 1 ) % RX_DESC_SIZE; + cprintf("e1000_receive return 0\n"); + return 0; +} + + +int +pci_e1000_attach(struct pci_func * pcif) +{ + int r; + // 使能E1000,分配 MMIO等 + pci_func_enable(pcif); + // 映射,并保存其虚拟地址,方便访问。 + e1000 = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + e1000_transmit_init(); + e1000_receive_init(); + cprintf("device status:[%08x]\n", e1000[E1000_LOCATE(E1000_DEVICE_STATUS)]); + return 0; +} + diff --git a/lab/kern/e1000.h b/lab/kern/e1000.h new file mode 100644 index 0000000..2e92dde --- /dev/null +++ b/lab/kern/e1000.h @@ -0,0 +1,135 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include + +#define PCI_E1000_VENDER_ID 0x8086 +#define PCI_E1000_DEVICE_ID 0x100E + +/* 循环队列的长度*/ +#define TX_DESC_SIZE 32 +#define TX_PACKET_SIZE 2048 + +#define RX_DESC_SIZE 128 +#define RX_PACKET_SIZE 2048 + +/* Register Set + * + * RW - register is both readable and writable + * + */ + +#define E1000_DEVICE_STATUS 0x00008 /* Device Status - RO */ + +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ + +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ + + + +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ + + + +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0X03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ + +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ + +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* Reserved */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* Reserved */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Reserved */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Reserved */ + + +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ + + + +/* Transmit Descriptor */ +struct E1000TxDesc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + +}__attribute__((packed)); + + + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ + + +#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */ +#define E1000_TXD_CMD_RS 0x08 /* Report Status */ + +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + + +/* Receive Descriptor */ +struct E1000RxDesc { + uint64_t buffer_addr; + uint16_t length; /* Data buffer length */ + uint16_t chksum; /* Check Sum */ + uint8_t status; + uint8_t err; + uint16_t special; +}; + +/* Transmit Descriptor bit definitions */ + + +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ + +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ + + +int e1000_transmit(void *addr, size_t len); +int e1000_receive(void *buf, size_t *len); + + +#endif // SOL >= 6 + + diff --git a/lab/kern/env.c b/lab/kern/env.c index 2f03e35..b40f467 100644 --- a/lab/kern/env.c +++ b/lab/kern/env.c @@ -274,7 +274,8 @@ env_alloc(struct Env **newenv_store, envid_t parent_id) env_free_list = e->env_link; *newenv_store = e; - cprintf(".%08x. new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + // cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + return 0; } @@ -416,6 +417,7 @@ env_create(uint8_t *binary, enum EnvType type) { // LAB 3: Your code here. + struct Env *newenv; int ret = 0; if ((ret = env_alloc(&newenv, 0)) < 0) { @@ -426,6 +428,7 @@ env_create(uint8_t *binary, enum EnvType type) newenv->env_tf.tf_eflags |= FL_IOPL_MASK; } load_icode(newenv, binary); + // If this is the file server (type == ENV_TYPE_FS) give it I/O privileges. // LAB 5: Your code here. } @@ -447,7 +450,8 @@ env_free(struct Env *e) lcr3(PADDR(kern_pgdir)); // Note the environment's demise. - cprintf(".%08x. free env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + // cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + // Flush all mapped pages in the user portion of the address space static_assert(UTOP % PTSIZE == 0); diff --git a/lab/kern/init.c b/lab/kern/init.c index 0086895..05ef462 100644 --- a/lab/kern/init.c +++ b/lab/kern/init.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include static void boot_aps(void); @@ -41,6 +43,10 @@ i386_init(void) // Lab 4 multitasking initialization functions pic_init(); + // Lab 6 hardware initialization functions + time_init(); + pci_init(); + // Acquire the big kernel lock before waking up APs // Your code here: lock_kernel(); @@ -50,14 +56,18 @@ i386_init(void) // Start fs. ENV_CREATE(fs_fs, ENV_TYPE_FS); +#if !defined(TEST_NO_NS) + // Start ns. + ENV_CREATE(net_ns, ENV_TYPE_NS); +#endif + #if defined(TEST) // Don't touch -- used by grading script! ENV_CREATE(TEST, ENV_TYPE_USER); #else // Touch all you want. - ENV_CREATE(user_icode, ENV_TYPE_USER); - - + //ENV_CREATE(user_sched, ENV_TYPE_USER); + ENV_CREATE(user_yield, ENV_TYPE_USER); #endif // TEST* // Should not be necessary - drains keyboard because interrupt has given up. diff --git a/lab/kern/pci.c b/lab/kern/pci.c new file mode 100644 index 0000000..3d88088 --- /dev/null +++ b/lab/kern/pci.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include + +// Flag to do "lspci" at bootup +static int pci_show_devs = 1; +static int pci_show_addrs = 0; + +// PCI "configuration mechanism one" +static uint32_t pci_conf1_addr_ioport = 0x0cf8; +static uint32_t pci_conf1_data_ioport = 0x0cfc; + +// Forward declarations +static int pci_bridge_attach(struct pci_func *pcif); + +int pci_e1000_attach(struct pci_func * pcif); + + +// PCI driver table +struct pci_driver { + uint32_t key1, key2; + int (*attachfn) (struct pci_func *pcif); +}; + +// pci_attach_class matches the class and subclass of a PCI device +struct pci_driver pci_attach_class[] = { + { PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI, &pci_bridge_attach }, + + { 0, 0, 0 }, +}; + +// pci_attach_vendor matches the vendor ID and device ID of a PCI device. key1 +// and key2 should be the vendor ID and device ID respectively +struct pci_driver pci_attach_vendor[] = { + { PCI_E1000_VENDER_ID, PCI_E1000_DEVICE_ID, &pci_e1000_attach}, + { 0, 0, 0 }, +}; + +static void +pci_conf1_set_addr(uint32_t bus, + uint32_t dev, + uint32_t func, + uint32_t offset) +{ + assert(bus < 256); + assert(dev < 32); + assert(func < 8); + assert(offset < 256); + assert((offset & 0x3) == 0); + + uint32_t v = (1 << 31) | // config-space + (bus << 16) | (dev << 11) | (func << 8) | (offset); + outl(pci_conf1_addr_ioport, v); +} + +static uint32_t +pci_conf_read(struct pci_func *f, uint32_t off) +{ + pci_conf1_set_addr(f->bus->busno, f->dev, f->func, off); + return inl(pci_conf1_data_ioport); +} + +static void +pci_conf_write(struct pci_func *f, uint32_t off, uint32_t v) +{ + pci_conf1_set_addr(f->bus->busno, f->dev, f->func, off); + outl(pci_conf1_data_ioport, v); +} + +static int __attribute__((warn_unused_result)) +pci_attach_match(uint32_t key1, uint32_t key2, + struct pci_driver *list, struct pci_func *pcif) +{ + uint32_t i; + + for (i = 0; list[i].attachfn; i++) { + if (list[i].key1 == key1 && list[i].key2 == key2) { + int r = list[i].attachfn(pcif); + if (r > 0) + return r; + if (r < 0) + cprintf("pci_attach_match: attaching " + "%x.%x (%p): e\n", + key1, key2, list[i].attachfn, r); + } + } + return 0; +} + +static int +pci_attach(struct pci_func *f) +{ + return + pci_attach_match(PCI_CLASS(f->dev_class), + PCI_SUBCLASS(f->dev_class), + &pci_attach_class[0], f) || + pci_attach_match(PCI_VENDOR(f->dev_id), + PCI_PRODUCT(f->dev_id), + &pci_attach_vendor[0], f); +} + +static const char *pci_class[] = +{ + [0x0] = "Unknown", + [0x1] = "Storage controller", + [0x2] = "Network controller", + [0x3] = "Display controller", + [0x4] = "Multimedia device", + [0x5] = "Memory controller", + [0x6] = "Bridge device", +}; + +static void +pci_print_func(struct pci_func *f) +{ + const char *class = pci_class[0]; + if (PCI_CLASS(f->dev_class) < ARRAY_SIZE(pci_class)) + class = pci_class[PCI_CLASS(f->dev_class)]; + + cprintf("PCI: %02x:%02x.%d: %04x:%04x: class: %x.%x (%s) irq: %d\n", + f->bus->busno, f->dev, f->func, + PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id), + PCI_CLASS(f->dev_class), PCI_SUBCLASS(f->dev_class), class, + f->irq_line); +} + +static int +pci_scan_bus(struct pci_bus *bus) +{ + int totaldev = 0; + struct pci_func df; + memset(&df, 0, sizeof(df)); + df.bus = bus; + + for (df.dev = 0; df.dev < 32; df.dev++) { + uint32_t bhlc = pci_conf_read(&df, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(bhlc) > 1) // Unsupported or no device + continue; + + totaldev++; + + struct pci_func f = df; + for (f.func = 0; f.func < (PCI_HDRTYPE_MULTIFN(bhlc) ? 8 : 1); + f.func++) { + struct pci_func af = f; + + af.dev_id = pci_conf_read(&f, PCI_ID_REG); + if (PCI_VENDOR(af.dev_id) == 0xffff) + continue; + + uint32_t intr = pci_conf_read(&af, PCI_INTERRUPT_REG); + af.irq_line = PCI_INTERRUPT_LINE(intr); + + af.dev_class = pci_conf_read(&af, PCI_CLASS_REG); + if (pci_show_devs) + pci_print_func(&af); + pci_attach(&af); + } + } + + return totaldev; +} + +static int +pci_bridge_attach(struct pci_func *pcif) +{ + uint32_t ioreg = pci_conf_read(pcif, PCI_BRIDGE_STATIO_REG); + uint32_t busreg = pci_conf_read(pcif, PCI_BRIDGE_BUS_REG); + + if (PCI_BRIDGE_IO_32BITS(ioreg)) { + cprintf("PCI: %02x:%02x.%d: 32-bit bridge IO not supported.\n", + pcif->bus->busno, pcif->dev, pcif->func); + return 0; + } + + struct pci_bus nbus; + memset(&nbus, 0, sizeof(nbus)); + nbus.parent_bridge = pcif; + nbus.busno = (busreg >> PCI_BRIDGE_BUS_SECONDARY_SHIFT) & 0xff; + + if (pci_show_devs) + cprintf("PCI: %02x:%02x.%d: bridge to PCI bus %d--%d\n", + pcif->bus->busno, pcif->dev, pcif->func, + nbus.busno, + (busreg >> PCI_BRIDGE_BUS_SUBORDINATE_SHIFT) & 0xff); + + pci_scan_bus(&nbus); + return 1; +} + +// External PCI subsystem interface + +void +pci_func_enable(struct pci_func *f) +{ + pci_conf_write(f, PCI_COMMAND_STATUS_REG, + PCI_COMMAND_IO_ENABLE | + PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE); + + uint32_t bar_width; + uint32_t bar; + for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; + bar += bar_width) + { + uint32_t oldv = pci_conf_read(f, bar); + + bar_width = 4; + pci_conf_write(f, bar, 0xffffffff); + uint32_t rv = pci_conf_read(f, bar); + + if (rv == 0) + continue; + + int regnum = PCI_MAPREG_NUM(bar); + uint32_t base, size; + if (PCI_MAPREG_TYPE(rv) == PCI_MAPREG_TYPE_MEM) { + if (PCI_MAPREG_MEM_TYPE(rv) == PCI_MAPREG_MEM_TYPE_64BIT) + bar_width = 8; + + size = PCI_MAPREG_MEM_SIZE(rv); + base = PCI_MAPREG_MEM_ADDR(oldv); + if (pci_show_addrs) + cprintf(" mem region %d: %d bytes at 0x%x\n", + regnum, size, base); + } else { + size = PCI_MAPREG_IO_SIZE(rv); + base = PCI_MAPREG_IO_ADDR(oldv); + if (pci_show_addrs) + cprintf(" io region %d: %d bytes at 0x%x\n", + regnum, size, base); + } + + pci_conf_write(f, bar, oldv); + f->reg_base[regnum] = base; + f->reg_size[regnum] = size; + + if (size && !base) + cprintf("PCI device %02x:%02x.%d (%04x:%04x) " + "may be misconfigured: " + "region %d: base 0x%x, size %d\n", + f->bus->busno, f->dev, f->func, + PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id), + regnum, base, size); + } + + cprintf("PCI function %02x:%02x.%d (%04x:%04x) enabled\n", + f->bus->busno, f->dev, f->func, + PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id)); +} + +int +pci_init(void) +{ + static struct pci_bus root_bus; + memset(&root_bus, 0, sizeof(root_bus)); + + return pci_scan_bus(&root_bus); +} diff --git a/lab/kern/pci.h b/lab/kern/pci.h new file mode 100644 index 0000000..06f510c --- /dev/null +++ b/lab/kern/pci.h @@ -0,0 +1,33 @@ +#ifndef JOS_KERN_PCI_H +#define JOS_KERN_PCI_H + +#include + +// PCI subsystem interface +enum { pci_res_bus, pci_res_mem, pci_res_io, pci_res_max }; + +struct pci_bus; + +struct pci_func { + struct pci_bus *bus; // Primary bus for bridges + + uint32_t dev; + uint32_t func; + + uint32_t dev_id; + uint32_t dev_class; + + uint32_t reg_base[6]; + uint32_t reg_size[6]; + uint8_t irq_line; +}; + +struct pci_bus { + struct pci_func *parent_bridge; + uint32_t busno; +}; + +int pci_init(void); +void pci_func_enable(struct pci_func *f); + +#endif diff --git a/lab/kern/pcireg.h b/lab/kern/pcireg.h new file mode 100644 index 0000000..da15e63 --- /dev/null +++ b/lab/kern/pcireg.h @@ -0,0 +1,710 @@ +/* $NetBSD: pcireg.h,v 1.45 2004/02/04 06:58:24 soren Exp $ */ + +/* + * Copyright (c) 1995, 1996, 1999, 2000 + * Christopher G. Demetriou. All rights reserved. + * Copyright (c) 1994, 1996 Charles M. Hannum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_PCI_PCIREG_H_ +#define _DEV_PCI_PCIREG_H_ + +/* + * Standardized PCI configuration information + * + * XXX This is not complete. + */ + +#include + +/* + * Device identification register; contains a vendor ID and a device ID. + */ +#define PCI_ID_REG 0x00 + +typedef uint16_t pci_vendor_id_t; +typedef uint16_t pci_product_id_t; + +#define PCI_VENDOR_SHIFT 0 +#define PCI_VENDOR_MASK 0xffff +#define PCI_VENDOR(id) \ + (((id) >> PCI_VENDOR_SHIFT) & PCI_VENDOR_MASK) + +#define PCI_PRODUCT_SHIFT 16 +#define PCI_PRODUCT_MASK 0xffff +#define PCI_PRODUCT(id) \ + (((id) >> PCI_PRODUCT_SHIFT) & PCI_PRODUCT_MASK) + +#define PCI_ID_CODE(vid,pid) \ + ((((vid) & PCI_VENDOR_MASK) << PCI_VENDOR_SHIFT) | \ + (((pid) & PCI_PRODUCT_MASK) << PCI_PRODUCT_SHIFT)) \ + +/* + * Command and status register. + */ +#define PCI_COMMAND_STATUS_REG 0x04 +#define PCI_COMMAND_SHIFT 0 +#define PCI_COMMAND_MASK 0xffff +#define PCI_STATUS_SHIFT 16 +#define PCI_STATUS_MASK 0xffff + +#define PCI_COMMAND_STATUS_CODE(cmd,stat) \ + ((((cmd) & PCI_COMMAND_MASK) >> PCI_COMMAND_SHIFT) | \ + (((stat) & PCI_STATUS_MASK) >> PCI_STATUS_SHIFT)) \ + +#define PCI_COMMAND_IO_ENABLE 0x00000001 +#define PCI_COMMAND_MEM_ENABLE 0x00000002 +#define PCI_COMMAND_MASTER_ENABLE 0x00000004 +#define PCI_COMMAND_SPECIAL_ENABLE 0x00000008 +#define PCI_COMMAND_INVALIDATE_ENABLE 0x00000010 +#define PCI_COMMAND_PALETTE_ENABLE 0x00000020 +#define PCI_COMMAND_PARITY_ENABLE 0x00000040 +#define PCI_COMMAND_STEPPING_ENABLE 0x00000080 +#define PCI_COMMAND_SERR_ENABLE 0x00000100 +#define PCI_COMMAND_BACKTOBACK_ENABLE 0x00000200 + +#define PCI_STATUS_CAPLIST_SUPPORT 0x00100000 +#define PCI_STATUS_66MHZ_SUPPORT 0x00200000 +#define PCI_STATUS_UDF_SUPPORT 0x00400000 +#define PCI_STATUS_BACKTOBACK_SUPPORT 0x00800000 +#define PCI_STATUS_PARITY_ERROR 0x01000000 +#define PCI_STATUS_DEVSEL_FAST 0x00000000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x02000000 +#define PCI_STATUS_DEVSEL_SLOW 0x04000000 +#define PCI_STATUS_DEVSEL_MASK 0x06000000 +#define PCI_STATUS_TARGET_TARGET_ABORT 0x08000000 +#define PCI_STATUS_MASTER_TARGET_ABORT 0x10000000 +#define PCI_STATUS_MASTER_ABORT 0x20000000 +#define PCI_STATUS_SPECIAL_ERROR 0x40000000 +#define PCI_STATUS_PARITY_DETECT 0x80000000 + +/* + * PCI Class and Revision Register; defines type and revision of device. + */ +#define PCI_CLASS_REG 0x08 + +typedef uint8_t pci_class_t; +typedef uint8_t pci_subclass_t; +typedef uint8_t pci_interface_t; +typedef uint8_t pci_revision_t; + +#define PCI_CLASS_SHIFT 24 +#define PCI_CLASS_MASK 0xff +#define PCI_CLASS(cr) \ + (((cr) >> PCI_CLASS_SHIFT) & PCI_CLASS_MASK) + +#define PCI_SUBCLASS_SHIFT 16 +#define PCI_SUBCLASS_MASK 0xff +#define PCI_SUBCLASS(cr) \ + (((cr) >> PCI_SUBCLASS_SHIFT) & PCI_SUBCLASS_MASK) + +#define PCI_INTERFACE_SHIFT 8 +#define PCI_INTERFACE_MASK 0xff +#define PCI_INTERFACE(cr) \ + (((cr) >> PCI_INTERFACE_SHIFT) & PCI_INTERFACE_MASK) + +#define PCI_REVISION_SHIFT 0 +#define PCI_REVISION_MASK 0xff +#define PCI_REVISION(cr) \ + (((cr) >> PCI_REVISION_SHIFT) & PCI_REVISION_MASK) + +#define PCI_CLASS_CODE(mainclass, subclass, interface) \ + ((((mainclass) & PCI_CLASS_MASK) << PCI_CLASS_SHIFT) | \ + (((subclass) & PCI_SUBCLASS_MASK) << PCI_SUBCLASS_SHIFT) | \ + (((interface) & PCI_INTERFACE_MASK) << PCI_INTERFACE_SHIFT)) + +/* base classes */ +#define PCI_CLASS_PREHISTORIC 0x00 +#define PCI_CLASS_MASS_STORAGE 0x01 +#define PCI_CLASS_NETWORK 0x02 +#define PCI_CLASS_DISPLAY 0x03 +#define PCI_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MEMORY 0x05 +#define PCI_CLASS_BRIDGE 0x06 +#define PCI_CLASS_COMMUNICATIONS 0x07 +#define PCI_CLASS_SYSTEM 0x08 +#define PCI_CLASS_INPUT 0x09 +#define PCI_CLASS_DOCK 0x0a +#define PCI_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_SERIALBUS 0x0c +#define PCI_CLASS_WIRELESS 0x0d +#define PCI_CLASS_I2O 0x0e +#define PCI_CLASS_SATCOM 0x0f +#define PCI_CLASS_CRYPTO 0x10 +#define PCI_CLASS_DASP 0x11 +#define PCI_CLASS_UNDEFINED 0xff + +/* 0x00 prehistoric subclasses */ +#define PCI_SUBCLASS_PREHISTORIC_MISC 0x00 +#define PCI_SUBCLASS_PREHISTORIC_VGA 0x01 + +/* 0x01 mass storage subclasses */ +#define PCI_SUBCLASS_MASS_STORAGE_SCSI 0x00 +#define PCI_SUBCLASS_MASS_STORAGE_IDE 0x01 +#define PCI_SUBCLASS_MASS_STORAGE_FLOPPY 0x02 +#define PCI_SUBCLASS_MASS_STORAGE_IPI 0x03 +#define PCI_SUBCLASS_MASS_STORAGE_RAID 0x04 +#define PCI_SUBCLASS_MASS_STORAGE_ATA 0x05 +#define PCI_SUBCLASS_MASS_STORAGE_SATA 0x06 +#define PCI_SUBCLASS_MASS_STORAGE_MISC 0x80 + +/* 0x02 network subclasses */ +#define PCI_SUBCLASS_NETWORK_ETHERNET 0x00 +#define PCI_SUBCLASS_NETWORK_TOKENRING 0x01 +#define PCI_SUBCLASS_NETWORK_FDDI 0x02 +#define PCI_SUBCLASS_NETWORK_ATM 0x03 +#define PCI_SUBCLASS_NETWORK_ISDN 0x04 +#define PCI_SUBCLASS_NETWORK_WORLDFIP 0x05 +#define PCI_SUBCLASS_NETWORK_PCIMGMULTICOMP 0x06 +#define PCI_SUBCLASS_NETWORK_MISC 0x80 + +/* 0x03 display subclasses */ +#define PCI_SUBCLASS_DISPLAY_VGA 0x00 +#define PCI_SUBCLASS_DISPLAY_XGA 0x01 +#define PCI_SUBCLASS_DISPLAY_3D 0x02 +#define PCI_SUBCLASS_DISPLAY_MISC 0x80 + +/* 0x04 multimedia subclasses */ +#define PCI_SUBCLASS_MULTIMEDIA_VIDEO 0x00 +#define PCI_SUBCLASS_MULTIMEDIA_AUDIO 0x01 +#define PCI_SUBCLASS_MULTIMEDIA_TELEPHONY 0x02 +#define PCI_SUBCLASS_MULTIMEDIA_MISC 0x80 + +/* 0x05 memory subclasses */ +#define PCI_SUBCLASS_MEMORY_RAM 0x00 +#define PCI_SUBCLASS_MEMORY_FLASH 0x01 +#define PCI_SUBCLASS_MEMORY_MISC 0x80 + +/* 0x06 bridge subclasses */ +#define PCI_SUBCLASS_BRIDGE_HOST 0x00 +#define PCI_SUBCLASS_BRIDGE_ISA 0x01 +#define PCI_SUBCLASS_BRIDGE_EISA 0x02 +#define PCI_SUBCLASS_BRIDGE_MC 0x03 /* XXX _MCA? */ +#define PCI_SUBCLASS_BRIDGE_PCI 0x04 +#define PCI_SUBCLASS_BRIDGE_PCMCIA 0x05 +#define PCI_SUBCLASS_BRIDGE_NUBUS 0x06 +#define PCI_SUBCLASS_BRIDGE_CARDBUS 0x07 +#define PCI_SUBCLASS_BRIDGE_RACEWAY 0x08 +#define PCI_SUBCLASS_BRIDGE_STPCI 0x09 +#define PCI_SUBCLASS_BRIDGE_INFINIBAND 0x0a +#define PCI_SUBCLASS_BRIDGE_MISC 0x80 + +/* 0x07 communications subclasses */ +#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00 +#define PCI_SUBCLASS_COMMUNICATIONS_PARALLEL 0x01 +#define PCI_SUBCLASS_COMMUNICATIONS_MPSERIAL 0x02 +#define PCI_SUBCLASS_COMMUNICATIONS_MODEM 0x03 +#define PCI_SUBCLASS_COMMUNICATIONS_GPIB 0x04 +#define PCI_SUBCLASS_COMMUNICATIONS_SMARTCARD 0x05 +#define PCI_SUBCLASS_COMMUNICATIONS_MISC 0x80 + +/* 0x08 system subclasses */ +#define PCI_SUBCLASS_SYSTEM_PIC 0x00 +#define PCI_SUBCLASS_SYSTEM_DMA 0x01 +#define PCI_SUBCLASS_SYSTEM_TIMER 0x02 +#define PCI_SUBCLASS_SYSTEM_RTC 0x03 +#define PCI_SUBCLASS_SYSTEM_PCIHOTPLUG 0x04 +#define PCI_SUBCLASS_SYSTEM_MISC 0x80 + +/* 0x09 input subclasses */ +#define PCI_SUBCLASS_INPUT_KEYBOARD 0x00 +#define PCI_SUBCLASS_INPUT_DIGITIZER 0x01 +#define PCI_SUBCLASS_INPUT_MOUSE 0x02 +#define PCI_SUBCLASS_INPUT_SCANNER 0x03 +#define PCI_SUBCLASS_INPUT_GAMEPORT 0x04 +#define PCI_SUBCLASS_INPUT_MISC 0x80 + +/* 0x0a dock subclasses */ +#define PCI_SUBCLASS_DOCK_GENERIC 0x00 +#define PCI_SUBCLASS_DOCK_MISC 0x80 + +/* 0x0b processor subclasses */ +#define PCI_SUBCLASS_PROCESSOR_386 0x00 +#define PCI_SUBCLASS_PROCESSOR_486 0x01 +#define PCI_SUBCLASS_PROCESSOR_PENTIUM 0x02 +#define PCI_SUBCLASS_PROCESSOR_ALPHA 0x10 +#define PCI_SUBCLASS_PROCESSOR_POWERPC 0x20 +#define PCI_SUBCLASS_PROCESSOR_MIPS 0x30 +#define PCI_SUBCLASS_PROCESSOR_COPROC 0x40 + +/* 0x0c serial bus subclasses */ +#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00 +#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x01 +#define PCI_SUBCLASS_SERIALBUS_SSA 0x02 +#define PCI_SUBCLASS_SERIALBUS_USB 0x03 +#define PCI_SUBCLASS_SERIALBUS_FIBER 0x04 /* XXX _FIBRECHANNEL */ +#define PCI_SUBCLASS_SERIALBUS_SMBUS 0x05 +#define PCI_SUBCLASS_SERIALBUS_INFINIBAND 0x06 +#define PCI_SUBCLASS_SERIALBUS_IPMI 0x07 +#define PCI_SUBCLASS_SERIALBUS_SERCOS 0x08 +#define PCI_SUBCLASS_SERIALBUS_CANBUS 0x09 + +/* 0x0d wireless subclasses */ +#define PCI_SUBCLASS_WIRELESS_IRDA 0x00 +#define PCI_SUBCLASS_WIRELESS_CONSUMERIR 0x01 +#define PCI_SUBCLASS_WIRELESS_RF 0x10 +#define PCI_SUBCLASS_WIRELESS_BLUETOOTH 0x11 +#define PCI_SUBCLASS_WIRELESS_BROADBAND 0x12 +#define PCI_SUBCLASS_WIRELESS_802_11A 0x20 +#define PCI_SUBCLASS_WIRELESS_802_11B 0x21 +#define PCI_SUBCLASS_WIRELESS_MISC 0x80 + +/* 0x0e I2O (Intelligent I/O) subclasses */ +#define PCI_SUBCLASS_I2O_STANDARD 0x00 + +/* 0x0f satellite communication subclasses */ +/* PCI_SUBCLASS_SATCOM_??? 0x00 / * XXX ??? */ +#define PCI_SUBCLASS_SATCOM_TV 0x01 +#define PCI_SUBCLASS_SATCOM_AUDIO 0x02 +#define PCI_SUBCLASS_SATCOM_VOICE 0x03 +#define PCI_SUBCLASS_SATCOM_DATA 0x04 + +/* 0x10 encryption/decryption subclasses */ +#define PCI_SUBCLASS_CRYPTO_NETCOMP 0x00 +#define PCI_SUBCLASS_CRYPTO_ENTERTAINMENT 0x10 +#define PCI_SUBCLASS_CRYPTO_MISC 0x80 + +/* 0x11 data acquisition and signal processing subclasses */ +#define PCI_SUBCLASS_DASP_DPIO 0x00 +#define PCI_SUBCLASS_DASP_TIMEFREQ 0x01 +#define PCI_SUBCLASS_DASP_SYNC 0x10 +#define PCI_SUBCLASS_DASP_MGMT 0x20 +#define PCI_SUBCLASS_DASP_MISC 0x80 + +/* + * PCI BIST/Header Type/Latency Timer/Cache Line Size Register. + */ +#define PCI_BHLC_REG 0x0c + +#define PCI_BIST_SHIFT 24 +#define PCI_BIST_MASK 0xff +#define PCI_BIST(bhlcr) \ + (((bhlcr) >> PCI_BIST_SHIFT) & PCI_BIST_MASK) + +#define PCI_HDRTYPE_SHIFT 16 +#define PCI_HDRTYPE_MASK 0xff +#define PCI_HDRTYPE(bhlcr) \ + (((bhlcr) >> PCI_HDRTYPE_SHIFT) & PCI_HDRTYPE_MASK) + +#define PCI_HDRTYPE_TYPE(bhlcr) \ + (PCI_HDRTYPE(bhlcr) & 0x7f) +#define PCI_HDRTYPE_MULTIFN(bhlcr) \ + ((PCI_HDRTYPE(bhlcr) & 0x80) != 0) + +#define PCI_LATTIMER_SHIFT 8 +#define PCI_LATTIMER_MASK 0xff +#define PCI_LATTIMER(bhlcr) \ + (((bhlcr) >> PCI_LATTIMER_SHIFT) & PCI_LATTIMER_MASK) + +#define PCI_CACHELINE_SHIFT 0 +#define PCI_CACHELINE_MASK 0xff +#define PCI_CACHELINE(bhlcr) \ + (((bhlcr) >> PCI_CACHELINE_SHIFT) & PCI_CACHELINE_MASK) + +#define PCI_BHLC_CODE(bist,type,multi,latency,cacheline) \ + ((((bist) & PCI_BIST_MASK) << PCI_BIST_SHIFT) | \ + (((type) & PCI_HDRTYPE_MASK) << PCI_HDRTYPE_SHIFT) | \ + (((multi)?0x80:0) << PCI_HDRTYPE_SHIFT) | \ + (((latency) & PCI_LATTIMER_MASK) << PCI_LATTIMER_SHIFT) | \ + (((cacheline) & PCI_CACHELINE_MASK) << PCI_CACHELINE_SHIFT)) + +/* + * PCI header type + */ +#define PCI_HDRTYPE_DEVICE 0 +#define PCI_HDRTYPE_PPB 1 +#define PCI_HDRTYPE_PCB 2 + +/* + * Mapping registers + */ +#define PCI_MAPREG_START 0x10 +#define PCI_MAPREG_END 0x28 +#define PCI_MAPREG_ROM 0x30 +#define PCI_MAPREG_PPB_END 0x18 +#define PCI_MAPREG_PCB_END 0x14 + +#define PCI_MAPREG_TYPE(mr) \ + ((mr) & PCI_MAPREG_TYPE_MASK) +#define PCI_MAPREG_TYPE_MASK 0x00000001 + +#define PCI_MAPREG_TYPE_MEM 0x00000000 +#define PCI_MAPREG_TYPE_IO 0x00000001 +#define PCI_MAPREG_ROM_ENABLE 0x00000001 + +#define PCI_MAPREG_MEM_TYPE(mr) \ + ((mr) & PCI_MAPREG_MEM_TYPE_MASK) +#define PCI_MAPREG_MEM_TYPE_MASK 0x00000006 + +#define PCI_MAPREG_MEM_TYPE_32BIT 0x00000000 +#define PCI_MAPREG_MEM_TYPE_32BIT_1M 0x00000002 +#define PCI_MAPREG_MEM_TYPE_64BIT 0x00000004 + +#define PCI_MAPREG_MEM_PREFETCHABLE(mr) \ + (((mr) & PCI_MAPREG_MEM_PREFETCHABLE_MASK) != 0) +#define PCI_MAPREG_MEM_PREFETCHABLE_MASK 0x00000008 + +#define PCI_MAPREG_MEM_ADDR(mr) \ + ((mr) & PCI_MAPREG_MEM_ADDR_MASK) +#define PCI_MAPREG_MEM_SIZE(mr) \ + (PCI_MAPREG_MEM_ADDR(mr) & -PCI_MAPREG_MEM_ADDR(mr)) +#define PCI_MAPREG_MEM_ADDR_MASK 0xfffffff0 + +#define PCI_MAPREG_MEM64_ADDR(mr) \ + ((mr) & PCI_MAPREG_MEM64_ADDR_MASK) +#define PCI_MAPREG_MEM64_SIZE(mr) \ + (PCI_MAPREG_MEM64_ADDR(mr) & -PCI_MAPREG_MEM64_ADDR(mr)) +#define PCI_MAPREG_MEM64_ADDR_MASK 0xfffffffffffffff0ULL + +#define PCI_MAPREG_IO_ADDR(mr) \ + ((mr) & PCI_MAPREG_IO_ADDR_MASK) +#define PCI_MAPREG_IO_SIZE(mr) \ + (PCI_MAPREG_IO_ADDR(mr) & -PCI_MAPREG_IO_ADDR(mr)) +#define PCI_MAPREG_IO_ADDR_MASK 0xfffffffc + +#define PCI_MAPREG_SIZE_TO_MASK(size) \ + (-(size)) + +#define PCI_MAPREG_NUM(offset) \ + (((unsigned)(offset)-PCI_MAPREG_START)/4) + + +/* + * Cardbus CIS pointer (PCI rev. 2.1) + */ +#define PCI_CARDBUS_CIS_REG 0x28 + +/* + * Subsystem identification register; contains a vendor ID and a device ID. + * Types/macros for PCI_ID_REG apply. + * (PCI rev. 2.1) + */ +#define PCI_SUBSYS_ID_REG 0x2c + +/* + * Capabilities link list (PCI rev. 2.2) + */ +#define PCI_CAPLISTPTR_REG 0x34 /* header type 0 */ +#define PCI_CARDBUS_CAPLISTPTR_REG 0x14 /* header type 2 */ +#define PCI_CAPLIST_PTR(cpr) ((cpr) & 0xff) +#define PCI_CAPLIST_NEXT(cr) (((cr) >> 8) & 0xff) +#define PCI_CAPLIST_CAP(cr) ((cr) & 0xff) + +#define PCI_CAP_RESERVED0 0x00 +#define PCI_CAP_PWRMGMT 0x01 +#define PCI_CAP_AGP 0x02 +#define PCI_CAP_AGP_MAJOR(cr) (((cr) >> 20) & 0xf) +#define PCI_CAP_AGP_MINOR(cr) (((cr) >> 16) & 0xf) +#define PCI_CAP_VPD 0x03 +#define PCI_CAP_SLOTID 0x04 +#define PCI_CAP_MSI 0x05 +#define PCI_CAP_CPCI_HOTSWAP 0x06 +#define PCI_CAP_PCIX 0x07 +#define PCI_CAP_LDT 0x08 +#define PCI_CAP_VENDSPEC 0x09 +#define PCI_CAP_DEBUGPORT 0x0a +#define PCI_CAP_CPCI_RSRCCTL 0x0b +#define PCI_CAP_HOTPLUG 0x0c +#define PCI_CAP_AGP8 0x0e +#define PCI_CAP_SECURE 0x0f +#define PCI_CAP_PCIEXPRESS 0x10 +#define PCI_CAP_MSIX 0x11 + +/* + * Vital Product Data; access via capability pointer (PCI rev 2.2). + */ +#define PCI_VPD_ADDRESS_MASK 0x7fff +#define PCI_VPD_ADDRESS_SHIFT 16 +#define PCI_VPD_ADDRESS(ofs) \ + (((ofs) & PCI_VPD_ADDRESS_MASK) << PCI_VPD_ADDRESS_SHIFT) +#define PCI_VPD_DATAREG(ofs) ((ofs) + 4) +#define PCI_VPD_OPFLAG 0x80000000 + +/* + * Power Management Capability; access via capability pointer. + */ + +/* Power Management Capability Register */ +#define PCI_PMCR 0x02 +#define PCI_PMCR_D1SUPP 0x0200 +#define PCI_PMCR_D2SUPP 0x0400 +/* Power Management Control Status Register */ +#define PCI_PMCSR 0x04 +#define PCI_PMCSR_STATE_MASK 0x03 +#define PCI_PMCSR_STATE_D0 0x00 +#define PCI_PMCSR_STATE_D1 0x01 +#define PCI_PMCSR_STATE_D2 0x02 +#define PCI_PMCSR_STATE_D3 0x03 + +/* + * PCI-X capability. + */ + +/* + * Command. 16 bits at offset 2 (e.g. upper 16 bits of the first 32-bit + * word at the capability; the lower 16 bits are the capability ID and + * next capability pointer). + * + * Since we always read PCI config space in 32-bit words, we define these + * as 32-bit values, offset and shifted appropriately. Make sure you perform + * the appropriate R/M/W cycles! + */ +#define PCI_PCIX_CMD 0x00 +#define PCI_PCIX_CMD_PERR_RECOVER 0x00010000 +#define PCI_PCIX_CMD_RELAXED_ORDER 0x00020000 +#define PCI_PCIX_CMD_BYTECNT_MASK 0x000c0000 +#define PCI_PCIX_CMD_BYTECNT_SHIFT 18 +#define PCI_PCIX_CMD_BCNT_512 0x00000000 +#define PCI_PCIX_CMD_BCNT_1024 0x00040000 +#define PCI_PCIX_CMD_BCNT_2048 0x00080000 +#define PCI_PCIX_CMD_BCNT_4096 0x000c0000 +#define PCI_PCIX_CMD_SPLTRANS_MASK 0x00700000 +#define PCI_PCIX_CMD_SPLTRANS_1 0x00000000 +#define PCI_PCIX_CMD_SPLTRANS_2 0x00100000 +#define PCI_PCIX_CMD_SPLTRANS_3 0x00200000 +#define PCI_PCIX_CMD_SPLTRANS_4 0x00300000 +#define PCI_PCIX_CMD_SPLTRANS_8 0x00400000 +#define PCI_PCIX_CMD_SPLTRANS_12 0x00500000 +#define PCI_PCIX_CMD_SPLTRANS_16 0x00600000 +#define PCI_PCIX_CMD_SPLTRANS_32 0x00700000 + +/* + * Status. 32 bits at offset 4. + */ +#define PCI_PCIX_STATUS 0x04 +#define PCI_PCIX_STATUS_FN_MASK 0x00000007 +#define PCI_PCIX_STATUS_DEV_MASK 0x000000f8 +#define PCI_PCIX_STATUS_BUS_MASK 0x0000ff00 +#define PCI_PCIX_STATUS_64BIT 0x00010000 +#define PCI_PCIX_STATUS_133 0x00020000 +#define PCI_PCIX_STATUS_SPLDISC 0x00040000 +#define PCI_PCIX_STATUS_SPLUNEX 0x00080000 +#define PCI_PCIX_STATUS_DEVCPLX 0x00100000 +#define PCI_PCIX_STATUS_MAXB_MASK 0x00600000 +#define PCI_PCIX_STATUS_MAXB_SHIFT 21 +#define PCI_PCIX_STATUS_MAXB_512 0x00000000 +#define PCI_PCIX_STATUS_MAXB_1024 0x00200000 +#define PCI_PCIX_STATUS_MAXB_2048 0x00400000 +#define PCI_PCIX_STATUS_MAXB_4096 0x00600000 +#define PCI_PCIX_STATUS_MAXST_MASK 0x03800000 +#define PCI_PCIX_STATUS_MAXST_1 0x00000000 +#define PCI_PCIX_STATUS_MAXST_2 0x00800000 +#define PCI_PCIX_STATUS_MAXST_3 0x01000000 +#define PCI_PCIX_STATUS_MAXST_4 0x01800000 +#define PCI_PCIX_STATUS_MAXST_8 0x02000000 +#define PCI_PCIX_STATUS_MAXST_12 0x02800000 +#define PCI_PCIX_STATUS_MAXST_16 0x03000000 +#define PCI_PCIX_STATUS_MAXST_32 0x03800000 +#define PCI_PCIX_STATUS_MAXRS_MASK 0x1c000000 +#define PCI_PCIX_STATUS_MAXRS_1K 0x00000000 +#define PCI_PCIX_STATUS_MAXRS_2K 0x04000000 +#define PCI_PCIX_STATUS_MAXRS_4K 0x08000000 +#define PCI_PCIX_STATUS_MAXRS_8K 0x0c000000 +#define PCI_PCIX_STATUS_MAXRS_16K 0x10000000 +#define PCI_PCIX_STATUS_MAXRS_32K 0x14000000 +#define PCI_PCIX_STATUS_MAXRS_64K 0x18000000 +#define PCI_PCIX_STATUS_MAXRS_128K 0x1c000000 +#define PCI_PCIX_STATUS_SCERR 0x20000000 + + +/* + * Interrupt Configuration Register; contains interrupt pin and line. + */ +#define PCI_INTERRUPT_REG 0x3c + +typedef uint8_t pci_intr_latency_t; +typedef uint8_t pci_intr_grant_t; +typedef uint8_t pci_intr_pin_t; +typedef uint8_t pci_intr_line_t; + +#define PCI_MAX_LAT_SHIFT 24 +#define PCI_MAX_LAT_MASK 0xff +#define PCI_MAX_LAT(icr) \ + (((icr) >> PCI_MAX_LAT_SHIFT) & PCI_MAX_LAT_MASK) + +#define PCI_MIN_GNT_SHIFT 16 +#define PCI_MIN_GNT_MASK 0xff +#define PCI_MIN_GNT(icr) \ + (((icr) >> PCI_MIN_GNT_SHIFT) & PCI_MIN_GNT_MASK) + +#define PCI_INTERRUPT_GRANT_SHIFT 24 +#define PCI_INTERRUPT_GRANT_MASK 0xff +#define PCI_INTERRUPT_GRANT(icr) \ + (((icr) >> PCI_INTERRUPT_GRANT_SHIFT) & PCI_INTERRUPT_GRANT_MASK) + +#define PCI_INTERRUPT_LATENCY_SHIFT 16 +#define PCI_INTERRUPT_LATENCY_MASK 0xff +#define PCI_INTERRUPT_LATENCY(icr) \ + (((icr) >> PCI_INTERRUPT_LATENCY_SHIFT) & PCI_INTERRUPT_LATENCY_MASK) + +#define PCI_INTERRUPT_PIN_SHIFT 8 +#define PCI_INTERRUPT_PIN_MASK 0xff +#define PCI_INTERRUPT_PIN(icr) \ + (((icr) >> PCI_INTERRUPT_PIN_SHIFT) & PCI_INTERRUPT_PIN_MASK) + +#define PCI_INTERRUPT_LINE_SHIFT 0 +#define PCI_INTERRUPT_LINE_MASK 0xff +#define PCI_INTERRUPT_LINE(icr) \ + (((icr) >> PCI_INTERRUPT_LINE_SHIFT) & PCI_INTERRUPT_LINE_MASK) + +#define PCI_INTERRUPT_CODE(lat,gnt,pin,line) \ + ((((lat)&PCI_INTERRUPT_LATENCY_MASK)<> 3) & 0xf) + +#define PCI_VPDRES_LARGE_NAME(x) ((x) & 0x7f) + +#define PCI_VPDRES_TYPE_COMPATIBLE_DEVICE_ID 0x3 /* small */ +#define PCI_VPDRES_TYPE_VENDOR_DEFINED 0xe /* small */ +#define PCI_VPDRES_TYPE_END_TAG 0xf /* small */ + +#define PCI_VPDRES_TYPE_IDENTIFIER_STRING 0x02 /* large */ +#define PCI_VPDRES_TYPE_VPD 0x10 /* large */ + +struct pci_vpd { + uint8_t vpd_key0; + uint8_t vpd_key1; + uint8_t vpd_len; /* length of data only */ + /* Actual data. */ +} __attribute__((__packed__)); + +/* + * Recommended VPD fields: + * + * PN Part number of assembly + * FN FRU part number + * EC EC level of assembly + * MN Manufacture ID + * SN Serial Number + * + * Conditionally recommended VPD fields: + * + * LI Load ID + * RL ROM Level + * RM Alterable ROM Level + * NA Network Address + * DD Device Driver Level + * DG Diagnostic Level + * LL Loadable Microcode Level + * VI Vendor ID/Device ID + * FU Function Number + * SI Subsystem Vendor ID/Subsystem ID + * + * Additional VPD fields: + * + * Z0-ZZ User/Product Specific + */ + +/* + * Threshold below which 32bit PCI DMA needs bouncing. + */ +#define PCI32_DMA_BOUNCE_THRESHOLD 0x100000000ULL + +#endif /* _DEV_PCI_PCIREG_H_ */ diff --git a/lab/kern/picirq.c b/lab/kern/picirq.c index 9f80bc7..3cd958a 100644 --- a/lab/kern/picirq.c +++ b/lab/kern/picirq.c @@ -84,3 +84,14 @@ irq_setmask_8259A(uint16_t mask) cprintf("\n"); } +void +irq_eoi(void) +{ + // OCW2: rse00xxx + // r: rotate + // s: specific + // e: end-of-interrupt + // xxx: specific interrupt line + outb(IO_PIC1, 0x20); + outb(IO_PIC2, 0x20); +} diff --git a/lab/kern/picirq.h b/lab/kern/picirq.h index 4734889..020c9ce 100644 --- a/lab/kern/picirq.h +++ b/lab/kern/picirq.h @@ -23,6 +23,7 @@ extern uint16_t irq_mask_8259A; void pic_init(void); void irq_setmask_8259A(uint16_t mask); +void irq_eoi(void); #endif // !__ASSEMBLER__ #endif // !JOS_KERN_PICIRQ_H diff --git a/lab/kern/syscall.c b/lab/kern/syscall.c index af027f1..621b055 100644 --- a/lab/kern/syscall.c +++ b/lab/kern/syscall.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include // Print a string to the system console. // The string is exactly 'len' characters long. @@ -55,11 +57,13 @@ sys_env_destroy(envid_t envid) if ((r = envid2env(envid, &e, 1)) < 0) return r; + if (e == curenv) cprintf("[%08x] exiting gracefully\n", curenv->env_id); else cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); return 0; } @@ -430,6 +434,30 @@ sys_ipc_recv(void *dstva) return 0; } +// Return the current time. +static int +sys_time_msec(void) +{ + // LAB 6: Your code here. + return time_msec(); + // panic("sys_time_msec not implemented"); +} + +int +sys_pkt_try_send(void * buf, size_t len) +{ + user_mem_assert(curenv, buf, len, PTE_U); + return e1000_transmit(buf, len); +} + +int +sys_pkt_try_receive(void *rev_buf, size_t *len) +{ + //cprintf("try recive...\n"); + return e1000_receive(rev_buf, len); +} + + // Dispatches to the correct kernel function, passing the arguments. int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) @@ -472,6 +500,13 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, case SYS_env_set_trapframe: return sys_env_set_trapframe((envid_t) a1, (struct Trapframe *) a2); + case SYS_time_msec: + return sys_time_msec(); + case SYS_pkt_try_send: + return sys_pkt_try_send((void *) a1, (size_t) a2); + case SYS_pkt_try_recv: + return sys_pkt_try_receive((void *) a1, (size_t *) a2); + case NSYSCALLS: return -E_INVAL; diff --git a/lab/kern/time.c b/lab/kern/time.c new file mode 100644 index 0000000..fd42c7d --- /dev/null +++ b/lab/kern/time.c @@ -0,0 +1,26 @@ +#include +#include + +static unsigned int ticks; + +void +time_init(void) +{ + ticks = 0; +} + +// This should be called once per timer interrupt. A timer interrupt +// fires every 10 ms. +void +time_tick(void) +{ + ticks++; + if (ticks * 10 < ticks) + panic("time_tick: time overflowed"); +} + +unsigned int +time_msec(void) +{ + return ticks * 10; +} diff --git a/lab/kern/time.h b/lab/kern/time.h new file mode 100644 index 0000000..ba92af1 --- /dev/null +++ b/lab/kern/time.h @@ -0,0 +1,11 @@ +#ifndef JOS_KERN_TIME_H +#define JOS_KERN_TIME_H +#ifndef JOS_KERNEL +# error "This is a JOS kernel header; user programs should not #include it" +#endif + +void time_init(void); +void time_tick(void); +unsigned int time_msec(void); + +#endif /* JOS_KERN_TIME_H */ diff --git a/lab/kern/trap.c b/lab/kern/trap.c index 1a78fa6..a0255f6 100644 --- a/lab/kern/trap.c +++ b/lab/kern/trap.c @@ -13,6 +13,7 @@ #include #include #include +#include static struct Taskstate ts; @@ -227,8 +228,6 @@ print_regs(struct PushRegs *regs) static void trap_dispatch(struct Trapframe *tf) { - // Handle processor exceptions. - // LAB 3: Your code here. switch(tf->tf_trapno) { case T_PGFLT: page_fault_handler(tf); @@ -251,6 +250,7 @@ trap_dispatch(struct Trapframe *tf) case (IRQ_OFFSET + IRQ_TIMER): // 回应8259A 接收中断。 lapic_eoi(); + time_tick(); sched_yield(); break; @@ -273,7 +273,6 @@ trap_dispatch(struct Trapframe *tf) return; } break; - } } diff --git a/lab/lab6.si4project/Backup/e1000(1028).c b/lab/lab6.si4project/Backup/e1000(1028).c new file mode 100644 index 0000000..3facf3f --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(1028).c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + + +// LAB 6: Your driver code here +#define E1000_LOCATE(offset) (offset >> 2) +volatile uint32_t *e1000; + +/* 为描述符列表分配静态内存 */ +struct E1000TxDesc tx_desc_list[TX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char pbuf[TX_DESC_SIZE][TX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +void +e1000_transmit_init() +{ + size_t i; + memset(tx_desc_list, 0 , sizeof(struct E1000TxDesc) * TX_DESC_SIZE); + for (i = 0; i < TX_DESC_SIZE; i++) { + tx_desc_list[i].buffer_addr = PADDR(pbuf[i]); + tx_desc_list[i].status = E1000_TXD_STAT_DD; + tx_desc_list[i].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; + + } + + e1000[E1000_LOCATE(E1000_TDBAL)] = PADDR(tx_desc_list); + e1000[E1000_LOCATE(E1000_TDBAH)] = 0; + e1000[E1000_LOCATE(E1000_TDLEN)] = sizeof(struct E1000TxDesc) * TX_DESC_SIZE; + // ensure that TDH and TDT are 0 index not offset + e1000[E1000_LOCATE(E1000_TDH)] = 0; + e1000[E1000_LOCATE(E1000_TDT)] = 0; + + // Initialize the Transmit Control Register (TCTL) + e1000[E1000_LOCATE(E1000_TCTL)] = E1000_TCTL_EN | + E1000_TCTL_PSP | + (E1000_TCTL_CT & (0x10 << 4)) | + (E1000_TCTL_COLD & (0x40 << 12)); + + // 10 8 6 + // 10 8 12 + e1000[E1000_LOCATE(E1000_TIPG)] = 10 | (8 << 10) | (12 << 20); +} + +int +e1000_transmit(void *addr, size_t len) +{ + size_t tdt = e1000[E1000_LOCATE(E1000_TDT)]; + struct E1000TxDesc *tail_desc = &tx_desc_list[tdt]; + + + if ( !(tail_desc->status & E1000_TXD_STAT_DD )) { + // Status is not DD + return -1; + } + memmove(pbuf[tdt], addr, len); + + tail_desc->length = (uint16_t )len; + // clear DD + tail_desc->status &= (~E1000_TXD_STAT_DD); + + e1000[E1000_LOCATE(E1000_TDT)] = (tdt+1) % TX_DESC_SIZE; + + return 0; + +} + +int +pci_e1000_attach(struct pci_func * pcif) +{ + int r; + // 使能E1000,分配 MMIO等 + pci_func_enable(pcif); + // 映射,并保存其虚拟地址,方便访问。 + e1000 = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + e1000_transmit_init(); + + cprintf("device status:[%08x]\n", e1000[E1000_LOCATE(E1000_DEVICE_STATUS)]); + /* + char mes[40] = "e1000 transmit :HELLO"; + for (size_t i = 0; i < 10 ; i++) { + e1000_transmit(mes, strlen(mes)); + } + */ + return 0; +} + diff --git a/lab/lab6.si4project/Backup/e1000(1108).h b/lab/lab6.si4project/Backup/e1000(1108).h new file mode 100644 index 0000000..2f305a3 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(1108).h @@ -0,0 +1,86 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include + +#define PCI_E1000_VENDOR_ID 0x8086 +#define PCI_E1000_DEVICE_ID 0x100E + +/* 循环队列的长度*/ +#define TX_DESC_SIZE 32 +#define TX_PACKET_SIZE 2048 +/* Register Set + * + * RW - register is both readable and writable + * + */ + +#define E1000_DEVICE_STATUS 0x00008 /* Device Status - RO */ + +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ + + +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0X03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ + +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ + +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* Reserved */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* Reserved */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Reserved */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Reserved */ + + + + + +/* Transmit Descriptor */ +struct E1000TxDesc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + +}__attribute__((packed)); + + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ + + +#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */ +#define E1000_TXD_CMD_RS 0x08 /* Report Status */ + +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + + + +int e1000_transmit(void *addr, size_t len); + + +#endif // SOL >= 6 + + diff --git a/lab/lab6.si4project/Backup/e1000(18).c b/lab/lab6.si4project/Backup/e1000(18).c new file mode 100644 index 0000000..3facf3f --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(18).c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + + +// LAB 6: Your driver code here +#define E1000_LOCATE(offset) (offset >> 2) +volatile uint32_t *e1000; + +/* 为描述符列表分配静态内存 */ +struct E1000TxDesc tx_desc_list[TX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char pbuf[TX_DESC_SIZE][TX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +void +e1000_transmit_init() +{ + size_t i; + memset(tx_desc_list, 0 , sizeof(struct E1000TxDesc) * TX_DESC_SIZE); + for (i = 0; i < TX_DESC_SIZE; i++) { + tx_desc_list[i].buffer_addr = PADDR(pbuf[i]); + tx_desc_list[i].status = E1000_TXD_STAT_DD; + tx_desc_list[i].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; + + } + + e1000[E1000_LOCATE(E1000_TDBAL)] = PADDR(tx_desc_list); + e1000[E1000_LOCATE(E1000_TDBAH)] = 0; + e1000[E1000_LOCATE(E1000_TDLEN)] = sizeof(struct E1000TxDesc) * TX_DESC_SIZE; + // ensure that TDH and TDT are 0 index not offset + e1000[E1000_LOCATE(E1000_TDH)] = 0; + e1000[E1000_LOCATE(E1000_TDT)] = 0; + + // Initialize the Transmit Control Register (TCTL) + e1000[E1000_LOCATE(E1000_TCTL)] = E1000_TCTL_EN | + E1000_TCTL_PSP | + (E1000_TCTL_CT & (0x10 << 4)) | + (E1000_TCTL_COLD & (0x40 << 12)); + + // 10 8 6 + // 10 8 12 + e1000[E1000_LOCATE(E1000_TIPG)] = 10 | (8 << 10) | (12 << 20); +} + +int +e1000_transmit(void *addr, size_t len) +{ + size_t tdt = e1000[E1000_LOCATE(E1000_TDT)]; + struct E1000TxDesc *tail_desc = &tx_desc_list[tdt]; + + + if ( !(tail_desc->status & E1000_TXD_STAT_DD )) { + // Status is not DD + return -1; + } + memmove(pbuf[tdt], addr, len); + + tail_desc->length = (uint16_t )len; + // clear DD + tail_desc->status &= (~E1000_TXD_STAT_DD); + + e1000[E1000_LOCATE(E1000_TDT)] = (tdt+1) % TX_DESC_SIZE; + + return 0; + +} + +int +pci_e1000_attach(struct pci_func * pcif) +{ + int r; + // 使能E1000,分配 MMIO等 + pci_func_enable(pcif); + // 映射,并保存其虚拟地址,方便访问。 + e1000 = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + e1000_transmit_init(); + + cprintf("device status:[%08x]\n", e1000[E1000_LOCATE(E1000_DEVICE_STATUS)]); + /* + char mes[40] = "e1000 transmit :HELLO"; + for (size_t i = 0; i < 10 ; i++) { + e1000_transmit(mes, strlen(mes)); + } + */ + return 0; +} + diff --git a/lab/lab6.si4project/Backup/e1000(2502).c b/lab/lab6.si4project/Backup/e1000(2502).c new file mode 100644 index 0000000..15814be --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(2502).c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + + +// LAB 6: Your driver code here +#define E1000_LOCATE(offset) (offset >> 2) +volatile uint32_t *e1000; + +/* 为描述符列表分配静态内存 */ +struct E1000TxDesc tx_desc_list[TX_DESC_SIZE]; +struct Packet pack_buf[TX_DESC_SIZE]; + + +void +e1000_transmit_init() +{ + size_t i; + memset(tx_desc_list, 0 , sizeof(struct E1000TxDesc) * TX_DESC_SIZE); + for (i = 0; i < TX_DESC_SIZE; i++) { + tx_desc_list[i].buffer_addr = PADDR(&pack_buf[i]); + tx_desc_list[i].status |= E1000_TXD_STAT_DD; + tx_desc_list[i].cmd |= E1000_TXD_CMD_RS; + + } + + e1000[E1000_LOCATE(E1000_TDBAL)] = PADDR(tx_desc_list); + e1000[E1000_LOCATE(E1000_TDBAH)] = 0; + e1000[E1000_LOCATE(E1000_TDLEN)] = sizeof(struct E1000TxDesc) * TX_DESC_SIZE; + // ensure that TDH and TDT are 0 + e1000[E1000_LOCATE(E1000_TDH)] = 0; + e1000[E1000_LOCATE(E1000_TDT)] = 0; + + // Initialize the Transmit Control Register (TCTL) + e1000[E1000_LOCATE(E1000_TCTL)] |= E1000_TCTL_EN; + e1000[E1000_LOCATE(E1000_TCTL)] |= E1000_TCTL_PSP; + e1000[E1000_LOCATE(E1000_TCTL)] |= (E1000_TCTL_CT & (0x10 << 4)); + e1000[E1000_LOCATE(E1000_TCTL)] |= (E1000_TCTL_CT & (0x40 << 12)); + + e1000[E1000_LOCATE(E1000_TIPG)] = 10 | (8 << 10) | (6 << 20); +} + +int +pci_e1000_attach(struct pci_func * pcif) +{ + int r; + // 使能E1000,分配 MMIO等 + pci_func_enable(pcif); + // 映射,并保存其虚拟地址,方便访问。 + e1000 = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + e1000_transmit_init(); + + cprintf("device status:[%08x]\n", e1000[E1000_LOCATE(E1000_DEVICE_STATUS)]); + + return 0; +} + diff --git a/lab/lab6.si4project/Backup/e1000(2669).h b/lab/lab6.si4project/Backup/e1000(2669).h new file mode 100644 index 0000000..2f305a3 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(2669).h @@ -0,0 +1,86 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include + +#define PCI_E1000_VENDOR_ID 0x8086 +#define PCI_E1000_DEVICE_ID 0x100E + +/* 循环队列的长度*/ +#define TX_DESC_SIZE 32 +#define TX_PACKET_SIZE 2048 +/* Register Set + * + * RW - register is both readable and writable + * + */ + +#define E1000_DEVICE_STATUS 0x00008 /* Device Status - RO */ + +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ + + +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0X03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ + +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ + +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* Reserved */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* Reserved */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Reserved */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Reserved */ + + + + + +/* Transmit Descriptor */ +struct E1000TxDesc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + +}__attribute__((packed)); + + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ + + +#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */ +#define E1000_TXD_CMD_RS 0x08 /* Report Status */ + +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + + + +int e1000_transmit(void *addr, size_t len); + + +#endif // SOL >= 6 + + diff --git a/lab/lab6.si4project/Backup/e1000(3017).h b/lab/lab6.si4project/Backup/e1000(3017).h new file mode 100644 index 0000000..885d141 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(3017).h @@ -0,0 +1,131 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include "kern/pci.h" + +#define E1000_VENDER_ID_82540EM 0x8086 +#define E1000_DEV_ID_82540EM 0x100E + +#define TXDESCS 32 +#define TX_PKT_SIZE 1518 + +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_CMD_EOP 0x00000001 /* End of Packet */ +#define E1000_TXD_CMD_RS 0x00000008 /* Report Status */ + + +#define RXDESCS 128 +#define RX_PKT_SIZE 1518 +#define E1000_RCTL 0x00100 +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ + +enum { + E_TRANSMIT_RETRY = 1, + E_RECEIVE_RETRY, +}; + + +/*transmit descriptor related*/ +struct e1000_tx_desc +{ + uint64_t addr; + uint16_t length; + uint8_t cso; + uint8_t cmd; + uint8_t status; + uint8_t css; + uint16_t special; +}__attribute__((packed)); + +struct e1000_tctl { + uint32_t rsv1: 1; + uint32_t en: 1; + uint32_t rsv2: 1; + uint32_t psp: 1; + uint32_t ct: 8; + uint32_t cold: 10; + uint32_t swxoff: 1; + uint32_t rsv3: 1; + uint32_t rtlc: 1; + uint32_t nrtu: 1; + uint32_t rsv4: 6; +}; + +struct e1000_tipg { + uint32_t ipgt: 10; + uint32_t ipgr1: 10; + uint32_t ipgr2: 10; + uint32_t rsv: 2; +}; + +struct e1000_tdt { + uint16_t tdt; + uint16_t rsv; +}; + +struct e1000_tdlen { + uint32_t zero: 7; + uint32_t len: 13; + uint32_t rsv: 12; +}; + +struct e1000_tdh { + uint16_t tdh; + uint16_t rsv; +}; + + +/*receive descriptor related*/ +struct e1000_rx_desc { + uint64_t addr; + uint16_t length; + uint16_t chksum; + uint8_t status; + uint8_t errors; + uint16_t special; +}__attribute__((packed)); + +struct e1000_rdlen { + unsigned zero: 7; + unsigned len: 13; + unsigned rsv: 12; +}; + +struct e1000_rdh { + uint16_t rdh; + uint16_t rsv; +}; + +struct e1000_rdt { + uint16_t rdt; + uint16_t rsv; +}; + + +int e1000_attachfn(struct pci_func *pcif); +static void e1000_transmit_init(); +int e1000_transmit(void *data, size_t len); + +static void e1000_receive_init(); +int e1000_receive(void *addr, size_t *len); + +#endif // JOS_KERN_E1000_H diff --git a/lab/lab6.si4project/Backup/e1000(3240).h b/lab/lab6.si4project/Backup/e1000(3240).h new file mode 100644 index 0000000..50d8b46 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(3240).h @@ -0,0 +1,94 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include + +#define PCI_E1000_VENDOR_ID 0x8086 +#define PCI_E1000_DEVICE_ID 0x100E + +/* 循环队列的长度*/ +#define TX_DESC_SIZE 32 +/* Register Set + * + * RW - register is both readable and writable + * + */ + +#define E1000_DEVICE_STATUS 0x00008 /* Device Status - RO */ + +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ + +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0X03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ + +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ + + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* Reserved */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* Reserved */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Reserved */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Reserved */ + + + + + +/* Transmit Descriptor */ +struct E1000TxDesc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + +}__attribute__((packed)); + + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + +/* packet buffer*/ +struct Packet { + char body[2048]; +}; + + + +#endif // SOL >= 6 + + diff --git a/lab/lab6.si4project/Backup/e1000(460).c b/lab/lab6.si4project/Backup/e1000(460).c new file mode 100644 index 0000000..baf43f8 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(460).c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include + + +// LAB 6: Your driver code here +#define E1000_LOCATE(offset) (offset >> 2) +volatile uint32_t *e1000; + +uint32_t E1000_MAC[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + +/* 为描述符列表分配静态内存 */ +struct E1000TxDesc tx_desc_list[TX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char tx_pbuf[TX_DESC_SIZE][TX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +struct E1000RxDesc rx_desc_list[RX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char rx_pbuf[RX_DESC_SIZE][RX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +void +e1000_transmit_init() +{ + size_t i; + memset(tx_desc_list, 0 , sizeof(struct E1000TxDesc) * TX_DESC_SIZE); + for (i = 0; i < TX_DESC_SIZE; i++) { + tx_desc_list[i].buffer_addr = PADDR(tx_pbuf[i]); + tx_desc_list[i].status = E1000_TXD_STAT_DD; + tx_desc_list[i].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; + + } + + e1000[E1000_LOCATE(E1000_TDBAL)] = PADDR(tx_desc_list); + e1000[E1000_LOCATE(E1000_TDBAH)] = 0; + e1000[E1000_LOCATE(E1000_TDLEN)] = sizeof(struct E1000TxDesc) * TX_DESC_SIZE; + // ensure that TDH and TDT are 0 index not offset + e1000[E1000_LOCATE(E1000_TDH)] = 0; + e1000[E1000_LOCATE(E1000_TDT)] = 0; + + // Initialize the Transmit Control Register (TCTL) + e1000[E1000_LOCATE(E1000_TCTL)] = E1000_TCTL_EN | + E1000_TCTL_PSP | + (E1000_TCTL_CT & (0x10 << 4)) | + (E1000_TCTL_COLD & (0x40 << 12)); + + // 10 8 6 + // 10 8 12 + e1000[E1000_LOCATE(E1000_TIPG)] = 10 | (8 << 10) | (12 << 20); +} + +int +e1000_transmit(void *addr, size_t len) +{ + size_t tdt = e1000[E1000_LOCATE(E1000_TDT)]; + struct E1000TxDesc *tail_desc = &tx_desc_list[tdt]; + + + if ( !(tail_desc->status & E1000_TXD_STAT_DD )) { + // Status is not DD + return -1; + } + memmove(tx_pbuf[tdt], addr, len); + + tail_desc->length = (uint16_t )len; + // clear DD + tail_desc->status &= (~E1000_TXD_STAT_DD); + + e1000[E1000_LOCATE(E1000_TDT)] = (tdt+1) % TX_DESC_SIZE; + + // cprintf("transmit a packet: %s", addr); + return 0; + +} + +void +e1000_set_mac_addr(uint32_t mac[]) +{ + uint32_t low = 0, high = 0; + int i; + + for (i = 0; i < 4; i++) { + low |= mac[i] << (8 * i); + } + + for (i = 4; i < 6; i++) { + high |= mac[i] << (8 * i); + } + + e1000[E1000_LOCATE(E1000_RA)] = low; + e1000[E1000_LOCATE(E1000_RA) + 1] = high | E1000_RAH_AV; +} + +void +e1000_receive_init() +{ + size_t i; + memset(rx_desc_list, 0 , sizeof(struct E1000RxDesc) * RX_DESC_SIZE); + for (i = 0; i < RX_DESC_SIZE; i++) { + rx_desc_list[i].buffer_addr = PADDR(rx_pbuf[i]); + } + e1000_set_mac_addr(E1000_MAC); + cprintf("mac addr %x:%x", e1000[E1000_LOCATE(E1000_RA)], e1000[E1000_LOCATE(E1000_RA) + 1] ); + e1000[E1000_LOCATE(E1000_ICS)] = 0; + e1000[E1000_LOCATE(E1000_IMS)] = 0; + e1000[E1000_LOCATE(E1000_RDBAL)] = PADDR(rx_desc_list); + e1000[E1000_LOCATE(E1000_RDBAH)] = 0; + + e1000[E1000_LOCATE(E1000_RDLEN)] = sizeof(struct E1000RxDesc) * RX_DESC_SIZE; + e1000[E1000_LOCATE(E1000_RDH)] = RX_DESC_SIZE - 1; + e1000[E1000_LOCATE(E1000_RDH)] = 0; + + e1000[E1000_LOCATE(E1000_RCTL)] = E1000_RCTL_EN | E1000_RCTL_SECRC | E1000_RCTL_BAM | E1000_RCTL_SZ_2048; + +} + +int e1000_receive(void *buf, size_t *len) +{ + static size_t next = 0; + size_t tail = e1000[E1000_LOCATE(E1000_RDT)]; + if ( !(rx_desc_list[next].status & E1000_RXD_STAT_DD) ) { + // cprintf("no packet\n"); + return -1; + } + *len = rx_desc_list[next].length; + memcpy(buf, rx_pbuf[next], *len); + + rx_desc_list[next].status &= ~E1000_RXD_STAT_DD; + next = (next + 1) % RX_DESC_SIZE; + e1000[E1000_LOCATE(E1000_RDT)] = (tail + 1 ) % RX_DESC_SIZE; + cprintf("e1000_receive return 0\n"); + return 0; +} + + +int +pci_e1000_attach(struct pci_func * pcif) +{ + int r; + // 使能E1000,分配 MMIO等 + pci_func_enable(pcif); + // 映射,并保存其虚拟地址,方便访问。 + e1000 = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + e1000_transmit_init(); + e1000_receive_init(); + cprintf("device status:[%08x]\n", e1000[E1000_LOCATE(E1000_DEVICE_STATUS)]); + return 0; +} + diff --git a/lab/lab6.si4project/Backup/e1000(5113).h b/lab/lab6.si4project/Backup/e1000(5113).h new file mode 100644 index 0000000..5b47dfb --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(5113).h @@ -0,0 +1,120 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include + +#define PCI_E1000_VENDOR_ID 0x8086 +#define PCI_E1000_DEVICE_ID 0x100E + +/* 循环队列的长度*/ +#define TX_DESC_SIZE 32 +#define TX_PACKET_SIZE 2048 + +#define RX_DESC_SIZE 128 +#define RX_PACKET_SIZE 2048 + +/* Register Set + * + * RW - register is both readable and writable + * + */ + +#define E1000_DEVICE_STATUS 0x00008 /* Device Status - RO */ + +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ + + + +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ + + + +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0X03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ + +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ + +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* Reserved */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* Reserved */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Reserved */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Reserved */ + + +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ + + + +/* Transmit Descriptor */ +struct E1000TxDesc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + +}__attribute__((packed)); + + + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ + + +#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */ +#define E1000_TXD_CMD_RS 0x08 /* Report Status */ + +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + + +/* Receive Descriptor */ +struct E1000RxDesc { + uint64_t buffer_addr; + uint16_t length; /* Data buffer length */ + uint16_t chksum; /* Check Sum */ + uint8_t status; + uint8_t err; + uint16_t special; +}; + +/* Transmit Descriptor bit definitions */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ + + + +int e1000_transmit(void *addr, size_t len); + + +#endif // SOL >= 6 + + diff --git a/lab/lab6.si4project/Backup/e1000(5347).h b/lab/lab6.si4project/Backup/e1000(5347).h new file mode 100644 index 0000000..885d141 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(5347).h @@ -0,0 +1,131 @@ +#ifndef JOS_KERN_E1000_H +#define JOS_KERN_E1000_H + +#include "kern/pci.h" + +#define E1000_VENDER_ID_82540EM 0x8086 +#define E1000_DEV_ID_82540EM 0x100E + +#define TXDESCS 32 +#define TX_PKT_SIZE 1518 + +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_CMD_EOP 0x00000001 /* End of Packet */ +#define E1000_TXD_CMD_RS 0x00000008 /* Report Status */ + + +#define RXDESCS 128 +#define RX_PKT_SIZE 1518 +#define E1000_RCTL 0x00100 +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ + +enum { + E_TRANSMIT_RETRY = 1, + E_RECEIVE_RETRY, +}; + + +/*transmit descriptor related*/ +struct e1000_tx_desc +{ + uint64_t addr; + uint16_t length; + uint8_t cso; + uint8_t cmd; + uint8_t status; + uint8_t css; + uint16_t special; +}__attribute__((packed)); + +struct e1000_tctl { + uint32_t rsv1: 1; + uint32_t en: 1; + uint32_t rsv2: 1; + uint32_t psp: 1; + uint32_t ct: 8; + uint32_t cold: 10; + uint32_t swxoff: 1; + uint32_t rsv3: 1; + uint32_t rtlc: 1; + uint32_t nrtu: 1; + uint32_t rsv4: 6; +}; + +struct e1000_tipg { + uint32_t ipgt: 10; + uint32_t ipgr1: 10; + uint32_t ipgr2: 10; + uint32_t rsv: 2; +}; + +struct e1000_tdt { + uint16_t tdt; + uint16_t rsv; +}; + +struct e1000_tdlen { + uint32_t zero: 7; + uint32_t len: 13; + uint32_t rsv: 12; +}; + +struct e1000_tdh { + uint16_t tdh; + uint16_t rsv; +}; + + +/*receive descriptor related*/ +struct e1000_rx_desc { + uint64_t addr; + uint16_t length; + uint16_t chksum; + uint8_t status; + uint8_t errors; + uint16_t special; +}__attribute__((packed)); + +struct e1000_rdlen { + unsigned zero: 7; + unsigned len: 13; + unsigned rsv: 12; +}; + +struct e1000_rdh { + uint16_t rdh; + uint16_t rsv; +}; + +struct e1000_rdt { + uint16_t rdt; + uint16_t rsv; +}; + + +int e1000_attachfn(struct pci_func *pcif); +static void e1000_transmit_init(); +int e1000_transmit(void *data, size_t len); + +static void e1000_receive_init(); +int e1000_receive(void *addr, size_t *len); + +#endif // JOS_KERN_E1000_H diff --git a/lab/lab6.si4project/Backup/e1000(5657).c b/lab/lab6.si4project/Backup/e1000(5657).c new file mode 100644 index 0000000..63935ef --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(5657).c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include + + +// LAB 6: Your driver code here +#define E1000_LOCATE(offset) (offset >> 2) +volatile uint32_t *e1000; + +uint32_t E1000_MAC[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + +/* 为描述符列表分配静态内存 */ +struct E1000TxDesc tx_desc_list[TX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char tx_pbuf[TX_DESC_SIZE][TX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +struct E1000RxDesc rx_desc_list[RX_DESC_SIZE] __attribute__((aligned (PGSIZE))) ; +char rx_pbuf[RX_DESC_SIZE][RX_PACKET_SIZE] __attribute__((aligned (PGSIZE))) ; + +void +e1000_transmit_init() +{ + size_t i; + memset(tx_desc_list, 0 , sizeof(struct E1000TxDesc) * TX_DESC_SIZE); + for (i = 0; i < TX_DESC_SIZE; i++) { + tx_desc_list[i].buffer_addr = PADDR(tx_pbuf[i]); + tx_desc_list[i].status = E1000_TXD_STAT_DD; + tx_desc_list[i].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; + + } + + e1000[E1000_LOCATE(E1000_TDBAL)] = PADDR(tx_desc_list); + e1000[E1000_LOCATE(E1000_TDBAH)] = 0; + e1000[E1000_LOCATE(E1000_TDLEN)] = sizeof(struct E1000TxDesc) * TX_DESC_SIZE; + // ensure that TDH and TDT are 0 index not offset + e1000[E1000_LOCATE(E1000_TDH)] = 0; + e1000[E1000_LOCATE(E1000_TDT)] = 0; + + // Initialize the Transmit Control Register (TCTL) + e1000[E1000_LOCATE(E1000_TCTL)] = E1000_TCTL_EN | + E1000_TCTL_PSP | + (E1000_TCTL_CT & (0x10 << 4)) | + (E1000_TCTL_COLD & (0x40 << 12)); + + // 10 8 6 + // 10 8 12 + e1000[E1000_LOCATE(E1000_TIPG)] = 10 | (8 << 10) | (12 << 20); +} + +int +e1000_transmit(void *addr, size_t len) +{ + size_t tdt = e1000[E1000_LOCATE(E1000_TDT)]; + struct E1000TxDesc *tail_desc = &tx_desc_list[tdt]; + + + if ( !(tail_desc->status & E1000_TXD_STAT_DD )) { + // Status is not DD + return -1; + } + memmove(tx_pbuf[tdt], addr, len); + + tail_desc->length = (uint16_t )len; + // clear DD + tail_desc->status &= (~E1000_TXD_STAT_DD); + + e1000[E1000_LOCATE(E1000_TDT)] = (tdt+1) % TX_DESC_SIZE; + + return 0; + +} + +void e1000_set_mac_addr(uint32_t mac[]) +{ + uint32_t low = 0, high = 0; + int i; + + for (i = 0; i < 4; i++) { + low |= mac[i] << (8 * i); + } + + for (i = 4; i < 6; i++) { + high |= mac[i] << (8 * i); + } + + e1000[E1000_LOCATE(E1000_RA)] = low; + e1000[E1000_LOCATE(E1000_RA) + 1] = high | E1000_RAH_AV; +} + +void e1000_receive_init() +{ + size_t i; + memset(rx_desc_list, 0 , sizeof(struct E1000RxDesc) * RX_DESC_SIZE); + for (i = 0; i < RX_DESC_SIZE; i++) { + rx_desc_list[i].buffer_addr = PADDR(rx_pbuf[i]); + } + + e1000[E1000_LOCATE(E1000_RDBAL)] = PADDR(rx_desc_list); + e1000[E1000_LOCATE(E1000_RDBAH)] = 0; + + e1000[E1000_LOCATE(E1000_RDLEN)] = sizeof(struct E1000RxDesc) * RX_DESC_SIZE; + + +} + +int +pci_e1000_attach(struct pci_func * pcif) +{ + int r; + // 使能E1000,分配 MMIO等 + pci_func_enable(pcif); + // 映射,并保存其虚拟地址,方便访问。 + e1000 = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + e1000_transmit_init(); + + cprintf("device status:[%08x]\n", e1000[E1000_LOCATE(E1000_DEVICE_STATUS)]); + /* + char mes[40] = "e1000 transmit :HELLO"; + for (size_t i = 0; i < 10 ; i++) { + e1000_transmit(mes, strlen(mes)); + } + */ + return 0; +} + diff --git a/lab/lab6.si4project/Backup/e1000(5817).c b/lab/lab6.si4project/Backup/e1000(5817).c new file mode 100644 index 0000000..d36c852 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(5817).c @@ -0,0 +1,162 @@ +#include +#include +#include + +// LAB 6: Your driver code here +volatile void *bar_va; +#define E1000REG(offset) (void *)(bar_va + offset) + +struct e1000_tdh *tdh; +struct e1000_tdt *tdt; +struct e1000_tx_desc tx_desc_array[TXDESCS]; +char tx_buffer_array[TXDESCS][TX_PKT_SIZE]; + +struct e1000_rdh *rdh; +struct e1000_rdt *rdt; +struct e1000_rx_desc rx_desc_array[RXDESCS]; +char rx_buffer_array[RXDESCS][RX_PKT_SIZE]; + +uint32_t E1000_MAC[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; +int +e1000_attachfn(struct pci_func *pcif) +{ + pci_func_enable(pcif); + cprintf("reg_base:%x, reg_size:%x\n", pcif->reg_base[0], pcif->reg_size[0]); + + bar_va = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + + uint32_t *status_reg = (uint32_t *)E1000REG(E1000_STATUS); + assert(*status_reg == 0x80080783); + + e1000_transmit_init(); + e1000_receive_init(); + + /* + * transmit test + */ + // char *data = "transmit test"; + // e1000_transmit(data, 13); + + return 0; +} + +static void +e1000_transmit_init() +{ + int i; + for (i = 0; i < TXDESCS; i++) { + tx_desc_array[i].addr = PADDR(tx_buffer_array[i]); + tx_desc_array[i].cmd = 0; + tx_desc_array[i].status |= E1000_TXD_STAT_DD; + } + + struct e1000_tdlen *tdlen = (struct e1000_tdlen *)E1000REG(E1000_TDLEN); + tdlen->len = TXDESCS; + + uint32_t *tdbal = (uint32_t *)E1000REG(E1000_TDBAL); + *tdbal = PADDR(tx_desc_array); + + uint32_t *tdbah = (uint32_t *)E1000REG(E1000_TDBAH); + *tdbah = 0; + + tdh = (struct e1000_tdh *)E1000REG(E1000_TDH); + tdh->tdh = 0; + + tdt = (struct e1000_tdt *)E1000REG(E1000_TDT); + tdt->tdt = 0; + + struct e1000_tctl *tctl = (struct e1000_tctl *)E1000REG(E1000_TCTL); + tctl->en = 1; + tctl->psp = 1; + tctl->ct = 0x10; + tctl->cold = 0x40; + + struct e1000_tipg *tipg = (struct e1000_tipg *)E1000REG(E1000_TIPG); + tipg->ipgt = 10; + tipg->ipgr1 = 4; + tipg->ipgr2 = 6; +} + +int +e1000_transmit(void *data, size_t len) +{ + uint32_t current = tdt->tdt; + if(!(tx_desc_array[current].status & E1000_TXD_STAT_DD)) { + return -E_TRANSMIT_RETRY; + } + tx_desc_array[current].length = len; + tx_desc_array[current].status &= ~E1000_TXD_STAT_DD; + tx_desc_array[current].cmd |= (E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS); + memcpy(tx_buffer_array[current], data, len); + uint32_t next = (current + 1) % TXDESCS; + tdt->tdt = next; + return 0; +} + +static void +get_ra_address(uint32_t mac[], uint32_t *ral, uint32_t *rah) +{ + uint32_t low = 0, high = 0; + int i; + + for (i = 0; i < 4; i++) { + low |= mac[i] << (8 * i); + } + + for (i = 4; i < 6; i++) { + high |= mac[i] << (8 * i); + } + + *ral = low; + *rah = high | E1000_RAH_AV; +} + +static void +e1000_receive_init() +{ + uint32_t *rdbal = (uint32_t *)E1000REG(E1000_RDBAL); + uint32_t *rdbah = (uint32_t *)E1000REG(E1000_RDBAH); + *rdbal = PADDR(rx_desc_array); + *rdbah = 0; + + int i; + for (i = 0; i < RXDESCS; i++) { + rx_desc_array[i].addr = PADDR(rx_buffer_array[i]); + } + + struct e1000_rdlen *rdlen = (struct e1000_rdlen *)E1000REG(E1000_RDLEN); + rdlen->len = RXDESCS; + + rdh = (struct e1000_rdh *)E1000REG(E1000_RDH); + rdt = (struct e1000_rdt *)E1000REG(E1000_RDT); + rdh->rdh = 0; + rdt->rdt = RXDESCS-1; + + uint32_t *rctl = (uint32_t *)E1000REG(E1000_RCTL); + *rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SECRC; + + uint32_t *ra = (uint32_t *)E1000REG(E1000_RA); + uint32_t ral, rah; + get_ra_address(E1000_MAC, &ral, &rah); + ra[0] = ral; + ra[1] = rah; +} + +int +e1000_receive(void *addr, size_t *len) +{ + static int32_t next = 0; + if(!(rx_desc_array[next].status & E1000_RXD_STAT_DD)) { + return -E_RECEIVE_RETRY; + } + if(rx_desc_array[next].errors) { + cprintf("receive errors\n"); + return -E_RECEIVE_RETRY; + } + *len = rx_desc_array[next].length; + memcpy(addr, rx_buffer_array[next], *len); + + rdt->rdt = (rdt->rdt + 1) % RXDESCS; + next = (next + 1) % RXDESCS; + return 0; +} diff --git a/lab/lab6.si4project/Backup/e1000(6672).c b/lab/lab6.si4project/Backup/e1000(6672).c new file mode 100644 index 0000000..d36c852 --- /dev/null +++ b/lab/lab6.si4project/Backup/e1000(6672).c @@ -0,0 +1,162 @@ +#include +#include +#include + +// LAB 6: Your driver code here +volatile void *bar_va; +#define E1000REG(offset) (void *)(bar_va + offset) + +struct e1000_tdh *tdh; +struct e1000_tdt *tdt; +struct e1000_tx_desc tx_desc_array[TXDESCS]; +char tx_buffer_array[TXDESCS][TX_PKT_SIZE]; + +struct e1000_rdh *rdh; +struct e1000_rdt *rdt; +struct e1000_rx_desc rx_desc_array[RXDESCS]; +char rx_buffer_array[RXDESCS][RX_PKT_SIZE]; + +uint32_t E1000_MAC[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; +int +e1000_attachfn(struct pci_func *pcif) +{ + pci_func_enable(pcif); + cprintf("reg_base:%x, reg_size:%x\n", pcif->reg_base[0], pcif->reg_size[0]); + + bar_va = mmio_map_region(pcif->reg_base[0], pcif->reg_size[0]); + + uint32_t *status_reg = (uint32_t *)E1000REG(E1000_STATUS); + assert(*status_reg == 0x80080783); + + e1000_transmit_init(); + e1000_receive_init(); + + /* + * transmit test + */ + // char *data = "transmit test"; + // e1000_transmit(data, 13); + + return 0; +} + +static void +e1000_transmit_init() +{ + int i; + for (i = 0; i < TXDESCS; i++) { + tx_desc_array[i].addr = PADDR(tx_buffer_array[i]); + tx_desc_array[i].cmd = 0; + tx_desc_array[i].status |= E1000_TXD_STAT_DD; + } + + struct e1000_tdlen *tdlen = (struct e1000_tdlen *)E1000REG(E1000_TDLEN); + tdlen->len = TXDESCS; + + uint32_t *tdbal = (uint32_t *)E1000REG(E1000_TDBAL); + *tdbal = PADDR(tx_desc_array); + + uint32_t *tdbah = (uint32_t *)E1000REG(E1000_TDBAH); + *tdbah = 0; + + tdh = (struct e1000_tdh *)E1000REG(E1000_TDH); + tdh->tdh = 0; + + tdt = (struct e1000_tdt *)E1000REG(E1000_TDT); + tdt->tdt = 0; + + struct e1000_tctl *tctl = (struct e1000_tctl *)E1000REG(E1000_TCTL); + tctl->en = 1; + tctl->psp = 1; + tctl->ct = 0x10; + tctl->cold = 0x40; + + struct e1000_tipg *tipg = (struct e1000_tipg *)E1000REG(E1000_TIPG); + tipg->ipgt = 10; + tipg->ipgr1 = 4; + tipg->ipgr2 = 6; +} + +int +e1000_transmit(void *data, size_t len) +{ + uint32_t current = tdt->tdt; + if(!(tx_desc_array[current].status & E1000_TXD_STAT_DD)) { + return -E_TRANSMIT_RETRY; + } + tx_desc_array[current].length = len; + tx_desc_array[current].status &= ~E1000_TXD_STAT_DD; + tx_desc_array[current].cmd |= (E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS); + memcpy(tx_buffer_array[current], data, len); + uint32_t next = (current + 1) % TXDESCS; + tdt->tdt = next; + return 0; +} + +static void +get_ra_address(uint32_t mac[], uint32_t *ral, uint32_t *rah) +{ + uint32_t low = 0, high = 0; + int i; + + for (i = 0; i < 4; i++) { + low |= mac[i] << (8 * i); + } + + for (i = 4; i < 6; i++) { + high |= mac[i] << (8 * i); + } + + *ral = low; + *rah = high | E1000_RAH_AV; +} + +static void +e1000_receive_init() +{ + uint32_t *rdbal = (uint32_t *)E1000REG(E1000_RDBAL); + uint32_t *rdbah = (uint32_t *)E1000REG(E1000_RDBAH); + *rdbal = PADDR(rx_desc_array); + *rdbah = 0; + + int i; + for (i = 0; i < RXDESCS; i++) { + rx_desc_array[i].addr = PADDR(rx_buffer_array[i]); + } + + struct e1000_rdlen *rdlen = (struct e1000_rdlen *)E1000REG(E1000_RDLEN); + rdlen->len = RXDESCS; + + rdh = (struct e1000_rdh *)E1000REG(E1000_RDH); + rdt = (struct e1000_rdt *)E1000REG(E1000_RDT); + rdh->rdh = 0; + rdt->rdt = RXDESCS-1; + + uint32_t *rctl = (uint32_t *)E1000REG(E1000_RCTL); + *rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SECRC; + + uint32_t *ra = (uint32_t *)E1000REG(E1000_RA); + uint32_t ral, rah; + get_ra_address(E1000_MAC, &ral, &rah); + ra[0] = ral; + ra[1] = rah; +} + +int +e1000_receive(void *addr, size_t *len) +{ + static int32_t next = 0; + if(!(rx_desc_array[next].status & E1000_RXD_STAT_DD)) { + return -E_RECEIVE_RETRY; + } + if(rx_desc_array[next].errors) { + cprintf("receive errors\n"); + return -E_RECEIVE_RETRY; + } + *len = rx_desc_array[next].length; + memcpy(addr, rx_buffer_array[next], *len); + + rdt->rdt = (rdt->rdt + 1) % RXDESCS; + next = (next + 1) % RXDESCS; + return 0; +} diff --git a/lab/lab6.si4project/Backup/httpd(174).c b/lab/lab6.si4project/Backup/httpd(174).c new file mode 100644 index 0000000..ede43bf --- /dev/null +++ b/lab/lab6.si4project/Backup/httpd(174).c @@ -0,0 +1,327 @@ +#include +#include +#include + +#define PORT 80 +#define VERSION "0.1" +#define HTTP_VERSION "1.0" + +#define E_BAD_REQ 1000 + +#define BUFFSIZE 512 +#define MAXPENDING 5 // Max connection requests + +struct http_request { + int sock; + char *url; + char *version; +}; + +struct responce_header { + int code; + char *header; +}; + +struct responce_header headers[] = { + { 200, "HTTP/" HTTP_VERSION " 200 OK\r\n" + "Server: jhttpd/" VERSION "\r\n"}, + {0, 0}, +}; + +struct error_messages { + int code; + char *msg; +}; + +struct error_messages errors[] = { + {400, "Bad Request"}, + {404, "Not Found"}, +}; + +static void +die(char *m) +{ + cprintf("%s\n", m); + exit(); +} + +static void +req_free(struct http_request *req) +{ + free(req->url); + free(req->version); +} + +static int +send_header(struct http_request *req, int code) +{ + struct responce_header *h = headers; + while (h->code != 0 && h->header!= 0) { + if (h->code == code) + break; + h++; + } + + if (h->code == 0) + return -1; + + int len = strlen(h->header); + if (write(req->sock, h->header, len) != len) { + die("Failed to send bytes to client"); + } + + return 0; +} + +static int +send_data(struct http_request *req, int fd) +{ + // LAB 6: Your code here. + panic("send_data not implemented"); +} + +static int +send_size(struct http_request *req, off_t size) +{ + char buf[64]; + int r; + + r = snprintf(buf, 64, "Content-Length: %ld\r\n", (long)size); + if (r > 63) + panic("buffer too small!"); + + if (write(req->sock, buf, r) != r) + return -1; + + return 0; +} + +static const char* +mime_type(const char *file) +{ + //TODO: for now only a single mime type + return "text/html"; +} + +static int +send_content_type(struct http_request *req) +{ + char buf[128]; + int r; + const char *type; + + type = mime_type(req->url); + if (!type) + return -1; + + r = snprintf(buf, 128, "Content-Type: %s\r\n", type); + if (r > 127) + panic("buffer too small!"); + + if (write(req->sock, buf, r) != r) + return -1; + + return 0; +} + +static int +send_header_fin(struct http_request *req) +{ + const char *fin = "\r\n"; + int fin_len = strlen(fin); + + if (write(req->sock, fin, fin_len) != fin_len) + return -1; + + return 0; +} + +// given a request, this function creates a struct http_request +static int +http_request_parse(struct http_request *req, char *request) +{ + const char *url; + const char *version; + int url_len, version_len; + + if (!req) + return -1; + + if (strncmp(request, "GET ", 4) != 0) + return -E_BAD_REQ; + + // skip GET + request += 4; + + // get the url + url = request; + while (*request && *request != ' ') + request++; + url_len = request - url; + + req->url = malloc(url_len + 1); + memmove(req->url, url, url_len); + req->url[url_len] = '\0'; + + // skip space + request++; + + version = request; + while (*request && *request != '\n') + request++; + version_len = request - version; + + req->version = malloc(version_len + 1); + memmove(req->version, version, version_len); + req->version[version_len] = '\0'; + + // no entity parsing + + return 0; +} + +static int +send_error(struct http_request *req, int code) +{ + char buf[512]; + int r; + + struct error_messages *e = errors; + while (e->code != 0 && e->msg != 0) { + if (e->code == code) + break; + e++; + } + + if (e->code == 0) + return -1; + + r = snprintf(buf, 512, "HTTP/" HTTP_VERSION" %d %s\r\n" + "Server: jhttpd/" VERSION "\r\n" + "Connection: close" + "Content-type: text/html\r\n" + "\r\n" + "

%d - %s

\r\n", + e->code, e->msg, e->code, e->msg); + + if (write(req->sock, buf, r) != r) + return -1; + + return 0; +} + +static int +send_file(struct http_request *req) +{ + int r; + off_t file_size = -1; + int fd; + + // open the requested url for reading + // if the file does not exist, send a 404 error using send_error + // if the file is a directory, send a 404 error using send_error + // set file_size to the size of the file + + // LAB 6: Your code here. + panic("send_file not implemented"); + + if ((r = send_header(req, 200)) < 0) + goto end; + + if ((r = send_size(req, file_size)) < 0) + goto end; + + if ((r = send_content_type(req)) < 0) + goto end; + + if ((r = send_header_fin(req)) < 0) + goto end; + + r = send_data(req, fd); + +end: + close(fd); + return r; +} + +static void +handle_client(int sock) +{ + struct http_request con_d; + int r; + char buffer[BUFFSIZE]; + int received = -1; + struct http_request *req = &con_d; + + while (1) + { + // Receive message + if ((received = read(sock, buffer, BUFFSIZE)) < 0) + panic("failed to read"); + + memset(req, 0, sizeof(*req)); + + req->sock = sock; + + r = http_request_parse(req, buffer); + if (r == -E_BAD_REQ) + send_error(req, 400); + else if (r < 0) + panic("parse failed"); + else + send_file(req); + + req_free(req); + + // no keep alive + break; + } + + close(sock); +} + +void +umain(int argc, char **argv) +{ + int serversock, clientsock; + struct sockaddr_in server, client; + + binaryname = "jhttpd"; + + // Create the TCP socket + if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + die("Failed to create socket"); + + // Construct the server sockaddr_in structure + memset(&server, 0, sizeof(server)); // Clear struct + server.sin_family = AF_INET; // Internet/IP + server.sin_addr.s_addr = htonl(INADDR_ANY); // IP address + server.sin_port = htons(PORT); // server port + + // Bind the server socket + if (bind(serversock, (struct sockaddr *) &server, + sizeof(server)) < 0) + { + die("Failed to bind the server socket"); + } + + // Listen on the server socket + if (listen(serversock, MAXPENDING) < 0) + die("Failed to listen on server socket"); + + cprintf("Waiting for http connections...\n"); + + while (1) { + unsigned int clientlen = sizeof(client); + // Wait for client connection + if ((clientsock = accept(serversock, + (struct sockaddr *) &client, + &clientlen)) < 0) + { + die("Failed to accept client connection"); + } + handle_client(clientsock); + } + + close(serversock); +} diff --git a/lab/lab6.si4project/Backup/input(2647).c b/lab/lab6.si4project/Backup/input(2647).c new file mode 100644 index 0000000..b505582 --- /dev/null +++ b/lab/lab6.si4project/Backup/input(2647).c @@ -0,0 +1,65 @@ +#include "ns.h" +#include + + +extern union Nsipc nsipcbuf; + + +void +sleep(int msec) +{ + unsigned now = sys_time_msec(); + unsigned end = now + msec; + + if ((int)now < 0 && (int)now > -MAXERROR) + panic("sys_time_msec: %e", (int)now); + if (end < now) + panic("sleep: wrap"); + + while (sys_time_msec() < end) + sys_yield(); +} + + + + +void +input(envid_t ns_envid) +{ + binaryname = "ns_input"; + + // LAB 6: Your code here: + // - read a packet from the device driver + // - send it to the network server + // Hint: When you IPC a page to the network server, it will be + // reading from it for a while, so don't immediately receive + // another packet in to the same physical page. + /* + size_t len; + char rev_buf[RX_PACKET_SIZE]; + size_t i = 0; + while(1) { + // 阻塞式读 + while ( sys_pkt_try_receive(rev_buf, &len) < 0) { + sys_yield(); + } + memcpy(nsipcbuf.pkt.jp_data, rev_buf, len); + nsipcbuf.pkt.jp_len = len; + + ipc_send(ns_envid, NSREQ_INPUT, &nsipcbuf, PTE_P|PTE_U); + sleep(50); + } + */ + size_t len; + char buf[RX_PKT_SIZE]; + while (1) { + if (sys_pkt_recv(buf, &len) < 0) { + continue; + } + memcpy(nsipcbuf.pkt.jp_data, buf, len); + nsipcbuf.pkt.jp_len = len; + ipc_send(ns_envid, NSREQ_INPUT, &nsipcbuf, PTE_P|PTE_U|PTE_W); + sleep(50); + } + +} diff --git a/lab/lab6.si4project/Backup/input(7104).c b/lab/lab6.si4project/Backup/input(7104).c new file mode 100644 index 0000000..4e08f0f --- /dev/null +++ b/lab/lab6.si4project/Backup/input(7104).c @@ -0,0 +1,16 @@ +#include "ns.h" + +extern union Nsipc nsipcbuf; + +void +input(envid_t ns_envid) +{ + binaryname = "ns_input"; + + // LAB 6: Your code here: + // - read a packet from the device driver + // - send it to the network server + // Hint: When you IPC a page to the network server, it will be + // reading from it for a while, so don't immediately receive + // another packet in to the same physical page. +} diff --git a/lab/lab6.si4project/Backup/lib(1933).h b/lab/lab6.si4project/Backup/lib(1933).h new file mode 100644 index 0000000..66740e8 --- /dev/null +++ b/lab/lab6.si4project/Backup/lib(1933).h @@ -0,0 +1,152 @@ +// Main public header file for our user-land support library, +// whose code lives in the lib directory. +// This library is roughly our OS's version of a standard C library, +// and is intended to be linked into all user-mode applications +// (NOT the kernel or boot loader). + +#ifndef JOS_INC_LIB_H +#define JOS_INC_LIB_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USED(x) (void)(x) + +// main user program +void umain(int argc, char **argv); + +// libmain.c or entry.S +extern const char *binaryname; +extern const volatile struct Env *thisenv; +extern const volatile struct Env envs[NENV]; +extern const volatile struct PageInfo pages[]; + +// exit.c +void exit(void); + +// pgfault.c +void set_pgfault_handler(void (*handler)(struct UTrapframe *utf)); + +// readline.c +char* readline(const char *buf); + +// syscall.c +void sys_cputs(const char *string, size_t len); +int sys_cgetc(void); +envid_t sys_getenvid(void); +int sys_env_destroy(envid_t); +void sys_yield(void); +static envid_t sys_exofork(void); +int sys_env_set_status(envid_t env, int status); +int sys_env_set_trapframe(envid_t env, struct Trapframe *tf); +int sys_env_set_pgfault_upcall(envid_t env, void *upcall); +int sys_page_alloc(envid_t env, void *pg, int perm); +int sys_page_map(envid_t src_env, void *src_pg, + envid_t dst_env, void *dst_pg, int perm); +int sys_page_unmap(envid_t env, void *pg); +int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm); +int sys_ipc_recv(void *rcv_pg); +unsigned int sys_time_msec(void); + +// This must be inlined. Exercise for reader: why? +static inline envid_t __attribute__((always_inline)) +sys_exofork(void) +{ + envid_t ret; + asm volatile("int %2" + : "=a" (ret) + : "a" (SYS_exofork), "i" (T_SYSCALL)); + return ret; +} + +// ipc.c +void ipc_send(envid_t to_env, uint32_t value, void *pg, int perm); +int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store); +envid_t ipc_find_env(enum EnvType type); + +// fork.c +#define PTE_SHARE 0x400 +envid_t fork(void); +envid_t sfork(void); // Challenge! + +// fd.c +int close(int fd); +ssize_t read(int fd, void *buf, size_t nbytes); +ssize_t write(int fd, const void *buf, size_t nbytes); +int seek(int fd, off_t offset); +void close_all(void); +ssize_t readn(int fd, void *buf, size_t nbytes); +int dup(int oldfd, int newfd); +int fstat(int fd, struct Stat *statbuf); +int stat(const char *path, struct Stat *statbuf); + +// file.c +int open(const char *path, int mode); +int ftruncate(int fd, off_t size); +int remove(const char *path); +int sync(void); + +// pageref.c +int pageref(void *addr); + +// sockets.c +int accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int bind(int s, struct sockaddr *name, socklen_t namelen); +int shutdown(int s, int how); +int connect(int s, const struct sockaddr *name, socklen_t namelen); +int listen(int s, int backlog); +int socket(int domain, int type, int protocol); + +// nsipc.c +int nsipc_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int nsipc_bind(int s, struct sockaddr *name, socklen_t namelen); +int nsipc_shutdown(int s, int how); +int nsipc_close(int s); +int nsipc_connect(int s, const struct sockaddr *name, socklen_t namelen); +int nsipc_listen(int s, int backlog); +int nsipc_recv(int s, void *mem, int len, unsigned int flags); +int nsipc_send(int s, const void *buf, int size, unsigned int flags); +int nsipc_socket(int domain, int type, int protocol); + +// spawn.c +envid_t spawn(const char *program, const char **argv); +envid_t spawnl(const char *program, const char *arg0, ...); + +// console.c +void cputchar(int c); +int getchar(void); +int iscons(int fd); +int opencons(void); + +// pipe.c +int pipe(int pipefds[2]); +int pipeisclosed(int pipefd); + +// wait.c +void wait(envid_t env); + +/* File open modes */ +#define O_RDONLY 0x0000 /* open for reading only */ +#define O_WRONLY 0x0001 /* open for writing only */ +#define O_RDWR 0x0002 /* open for reading and writing */ +#define O_ACCMODE 0x0003 /* mask for above modes */ + +#define O_CREAT 0x0100 /* create if nonexistent */ +#define O_TRUNC 0x0200 /* truncate to zero length */ +#define O_EXCL 0x0400 /* error if already exists */ +#define O_MKDIR 0x0800 /* create directory, not regular file */ + +#endif // !JOS_INC_LIB_H diff --git a/lab/lab6.si4project/Backup/output(6468).c b/lab/lab6.si4project/Backup/output(6468).c new file mode 100644 index 0000000..896a161 --- /dev/null +++ b/lab/lab6.si4project/Backup/output(6468).c @@ -0,0 +1,27 @@ +#include "ns.h" + +extern union Nsipc nsipcbuf; + +void +output(envid_t ns_envid) +{ + binaryname = "ns_output"; + envid_t envid; + int perm; + int32_t req; + // LAB 6: Your code here: + // - read a packet from the network server + while(1) { + req = ipc_recv(&envid, (void *) &nsipcbuf, &perm); + if (req != NSREQ_OUTPUT) + continue; + + struct jif_pkt *pkt = &(nsipcbuf.pkt); + while (sys_pkt_try_send(pkt->jp_data, pkt->jp_len) < 0) { + sys_yield(); + + } + } + + +} diff --git a/lab/lab6.si4project/Backup/output(770).c b/lab/lab6.si4project/Backup/output(770).c new file mode 100644 index 0000000..f577c4e --- /dev/null +++ b/lab/lab6.si4project/Backup/output(770).c @@ -0,0 +1,13 @@ +#include "ns.h" + +extern union Nsipc nsipcbuf; + +void +output(envid_t ns_envid) +{ + binaryname = "ns_output"; + + // LAB 6: Your code here: + // - read a packet from the network server + // - send the packet to the device driver +} diff --git a/lab/lab6.si4project/Backup/pci(874).c b/lab/lab6.si4project/Backup/pci(874).c new file mode 100644 index 0000000..2655436 --- /dev/null +++ b/lab/lab6.si4project/Backup/pci(874).c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include + +// Flag to do "lspci" at bootup +static int pci_show_devs = 1; +static int pci_show_addrs = 0; + +// PCI "configuration mechanism one" +static uint32_t pci_conf1_addr_ioport = 0x0cf8; +static uint32_t pci_conf1_data_ioport = 0x0cfc; + +// Forward declarations +static int pci_bridge_attach(struct pci_func *pcif); + +int pci_e1000_attach(struct pci_func * pcif); + + +// PCI driver table +struct pci_driver { + uint32_t key1, key2; + int (*attachfn) (struct pci_func *pcif); +}; + +// pci_attach_class matches the class and subclass of a PCI device +struct pci_driver pci_attach_class[] = { + { PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI, &pci_bridge_attach }, + + { 0, 0, 0 }, +}; + +// pci_attach_vendor matches the vendor ID and device ID of a PCI device. key1 +// and key2 should be the vendor ID and device ID respectively +struct pci_driver pci_attach_vendor[] = { + { PCI_E1000_VENDOR_ID, PCI_E1000_DEVICE_ID, &pci_e1000_attach}, + { 0, 0, 0 }, +}; + +static void +pci_conf1_set_addr(uint32_t bus, + uint32_t dev, + uint32_t func, + uint32_t offset) +{ + assert(bus < 256); + assert(dev < 32); + assert(func < 8); + assert(offset < 256); + assert((offset & 0x3) == 0); + + uint32_t v = (1 << 31) | // config-space + (bus << 16) | (dev << 11) | (func << 8) | (offset); + outl(pci_conf1_addr_ioport, v); +} + +static uint32_t +pci_conf_read(struct pci_func *f, uint32_t off) +{ + pci_conf1_set_addr(f->bus->busno, f->dev, f->func, off); + return inl(pci_conf1_data_ioport); +} + +static void +pci_conf_write(struct pci_func *f, uint32_t off, uint32_t v) +{ + pci_conf1_set_addr(f->bus->busno, f->dev, f->func, off); + outl(pci_conf1_data_ioport, v); +} + +static int __attribute__((warn_unused_result)) +pci_attach_match(uint32_t key1, uint32_t key2, + struct pci_driver *list, struct pci_func *pcif) +{ + uint32_t i; + + for (i = 0; list[i].attachfn; i++) { + if (list[i].key1 == key1 && list[i].key2 == key2) { + int r = list[i].attachfn(pcif); + if (r > 0) + return r; + if (r < 0) + cprintf("pci_attach_match: attaching " + "%x.%x (%p): e\n", + key1, key2, list[i].attachfn, r); + } + } + return 0; +} + +static int +pci_attach(struct pci_func *f) +{ + return + pci_attach_match(PCI_CLASS(f->dev_class), + PCI_SUBCLASS(f->dev_class), + &pci_attach_class[0], f) || + pci_attach_match(PCI_VENDOR(f->dev_id), + PCI_PRODUCT(f->dev_id), + &pci_attach_vendor[0], f); +} + +static const char *pci_class[] = +{ + [0x0] = "Unknown", + [0x1] = "Storage controller", + [0x2] = "Network controller", + [0x3] = "Display controller", + [0x4] = "Multimedia device", + [0x5] = "Memory controller", + [0x6] = "Bridge device", +}; + +static void +pci_print_func(struct pci_func *f) +{ + const char *class = pci_class[0]; + if (PCI_CLASS(f->dev_class) < ARRAY_SIZE(pci_class)) + class = pci_class[PCI_CLASS(f->dev_class)]; + + cprintf("PCI: %02x:%02x.%d: %04x:%04x: class: %x.%x (%s) irq: %d\n", + f->bus->busno, f->dev, f->func, + PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id), + PCI_CLASS(f->dev_class), PCI_SUBCLASS(f->dev_class), class, + f->irq_line); +} + +static int +pci_scan_bus(struct pci_bus *bus) +{ + int totaldev = 0; + struct pci_func df; + memset(&df, 0, sizeof(df)); + df.bus = bus; + + for (df.dev = 0; df.dev < 32; df.dev++) { + uint32_t bhlc = pci_conf_read(&df, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(bhlc) > 1) // Unsupported or no device + continue; + + totaldev++; + + struct pci_func f = df; + for (f.func = 0; f.func < (PCI_HDRTYPE_MULTIFN(bhlc) ? 8 : 1); + f.func++) { + struct pci_func af = f; + + af.dev_id = pci_conf_read(&f, PCI_ID_REG); + if (PCI_VENDOR(af.dev_id) == 0xffff) + continue; + + uint32_t intr = pci_conf_read(&af, PCI_INTERRUPT_REG); + af.irq_line = PCI_INTERRUPT_LINE(intr); + + af.dev_class = pci_conf_read(&af, PCI_CLASS_REG); + if (pci_show_devs) + pci_print_func(&af); + pci_attach(&af); + } + } + + return totaldev; +} + +static int +pci_bridge_attach(struct pci_func *pcif) +{ + uint32_t ioreg = pci_conf_read(pcif, PCI_BRIDGE_STATIO_REG); + uint32_t busreg = pci_conf_read(pcif, PCI_BRIDGE_BUS_REG); + + if (PCI_BRIDGE_IO_32BITS(ioreg)) { + cprintf("PCI: %02x:%02x.%d: 32-bit bridge IO not supported.\n", + pcif->bus->busno, pcif->dev, pcif->func); + return 0; + } + + struct pci_bus nbus; + memset(&nbus, 0, sizeof(nbus)); + nbus.parent_bridge = pcif; + nbus.busno = (busreg >> PCI_BRIDGE_BUS_SECONDARY_SHIFT) & 0xff; + + if (pci_show_devs) + cprintf("PCI: %02x:%02x.%d: bridge to PCI bus %d--%d\n", + pcif->bus->busno, pcif->dev, pcif->func, + nbus.busno, + (busreg >> PCI_BRIDGE_BUS_SUBORDINATE_SHIFT) & 0xff); + + pci_scan_bus(&nbus); + return 1; +} + +// External PCI subsystem interface + +void +pci_func_enable(struct pci_func *f) +{ + pci_conf_write(f, PCI_COMMAND_STATUS_REG, + PCI_COMMAND_IO_ENABLE | + PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE); + + uint32_t bar_width; + uint32_t bar; + for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; + bar += bar_width) + { + uint32_t oldv = pci_conf_read(f, bar); + + bar_width = 4; + pci_conf_write(f, bar, 0xffffffff); + uint32_t rv = pci_conf_read(f, bar); + + if (rv == 0) + continue; + + int regnum = PCI_MAPREG_NUM(bar); + uint32_t base, size; + if (PCI_MAPREG_TYPE(rv) == PCI_MAPREG_TYPE_MEM) { + if (PCI_MAPREG_MEM_TYPE(rv) == PCI_MAPREG_MEM_TYPE_64BIT) + bar_width = 8; + + size = PCI_MAPREG_MEM_SIZE(rv); + base = PCI_MAPREG_MEM_ADDR(oldv); + if (pci_show_addrs) + cprintf(" mem region %d: %d bytes at 0x%x\n", + regnum, size, base); + } else { + size = PCI_MAPREG_IO_SIZE(rv); + base = PCI_MAPREG_IO_ADDR(oldv); + if (pci_show_addrs) + cprintf(" io region %d: %d bytes at 0x%x\n", + regnum, size, base); + } + + pci_conf_write(f, bar, oldv); + f->reg_base[regnum] = base; + f->reg_size[regnum] = size; + + if (size && !base) + cprintf("PCI device %02x:%02x.%d (%04x:%04x) " + "may be misconfigured: " + "region %d: base 0x%x, size %d\n", + f->bus->busno, f->dev, f->func, + PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id), + regnum, base, size); + } + + cprintf("PCI function %02x:%02x.%d (%04x:%04x) enabled\n", + f->bus->busno, f->dev, f->func, + PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id)); +} + +int +pci_init(void) +{ + static struct pci_bus root_bus; + memset(&root_bus, 0, sizeof(root_bus)); + + return pci_scan_bus(&root_bus); +} diff --git a/lab/lab6.si4project/Backup/syscall(2053).h b/lab/lab6.si4project/Backup/syscall(2053).h new file mode 100644 index 0000000..36f26de --- /dev/null +++ b/lab/lab6.si4project/Backup/syscall(2053).h @@ -0,0 +1,24 @@ +#ifndef JOS_INC_SYSCALL_H +#define JOS_INC_SYSCALL_H + +/* system call numbers */ +enum { + SYS_cputs = 0, + SYS_cgetc, + SYS_getenvid, + SYS_env_destroy, + SYS_page_alloc, + SYS_page_map, + SYS_page_unmap, + SYS_exofork, + SYS_env_set_status, + SYS_env_set_trapframe, + SYS_env_set_pgfault_upcall, + SYS_yield, + SYS_ipc_try_send, + SYS_ipc_recv, + SYS_time_msec, + NSYSCALLS +}; + +#endif /* !JOS_INC_SYSCALL_H */ diff --git a/lab/lab6.si4project/Backup/syscall(3019).c b/lab/lab6.si4project/Backup/syscall(3019).c new file mode 100644 index 0000000..9e1a1d9 --- /dev/null +++ b/lab/lab6.si4project/Backup/syscall(3019).c @@ -0,0 +1,124 @@ +// System call stubs. + +#include +#include + +static inline int32_t +syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) +{ + int32_t ret; + + // Generic system call: pass system call number in AX, + // up to five parameters in DX, CX, BX, DI, SI. + // Interrupt kernel with T_SYSCALL. + // + // The "volatile" tells the assembler not to optimize + // this instruction away just because we don't use the + // return value. + // + // The last clause tells the assembler that this can + // potentially change the condition codes and arbitrary + // memory locations. + + asm volatile("int %1\n" + : "=a" (ret) + : "i" (T_SYSCALL), + "a" (num), + "d" (a1), + "c" (a2), + "b" (a3), + "D" (a4), + "S" (a5) + : "cc", "memory"); + + if(check && ret > 0) + panic("syscall %d returned %d (> 0)", num, ret); + + return ret; +} + +void +sys_cputs(const char *s, size_t len) +{ + syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0); +} + +int +sys_cgetc(void) +{ + return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0); +} + +int +sys_env_destroy(envid_t envid) +{ + return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0); +} + +envid_t +sys_getenvid(void) +{ + return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0); +} + +void +sys_yield(void) +{ + syscall(SYS_yield, 0, 0, 0, 0, 0, 0); +} + +int +sys_page_alloc(envid_t envid, void *va, int perm) +{ + return syscall(SYS_page_alloc, 1, envid, (uint32_t) va, perm, 0, 0); +} + +int +sys_page_map(envid_t srcenv, void *srcva, envid_t dstenv, void *dstva, int perm) +{ + return syscall(SYS_page_map, 1, srcenv, (uint32_t) srcva, dstenv, (uint32_t) dstva, perm); +} + +int +sys_page_unmap(envid_t envid, void *va) +{ + return syscall(SYS_page_unmap, 1, envid, (uint32_t) va, 0, 0, 0); +} + +// sys_exofork is inlined in lib.h + +int +sys_env_set_status(envid_t envid, int status) +{ + return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0); +} + +int +sys_env_set_trapframe(envid_t envid, struct Trapframe *tf) +{ + return syscall(SYS_env_set_trapframe, 1, envid, (uint32_t) tf, 0, 0, 0); +} + +int +sys_env_set_pgfault_upcall(envid_t envid, void *upcall) +{ + return syscall(SYS_env_set_pgfault_upcall, 1, envid, (uint32_t) upcall, 0, 0, 0); +} + +int +sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, int perm) +{ + return syscall(SYS_ipc_try_send, 0, envid, value, (uint32_t) srcva, perm, 0); +} + +int +sys_ipc_recv(void *dstva) +{ + return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0); +} + +unsigned int +sys_time_msec(void) +{ + return (unsigned int) syscall(SYS_time_msec, 0, 0, 0, 0, 0, 0); +} diff --git a/lab/Untitled Project.si4project/Backup/syscall(8111).c b/lab/lab6.si4project/Backup/syscall(5120).c similarity index 95% rename from lab/Untitled Project.si4project/Backup/syscall(8111).c rename to lab/lab6.si4project/Backup/syscall(5120).c index d28128c..36ee169 100644 --- a/lab/Untitled Project.si4project/Backup/syscall(8111).c +++ b/lab/lab6.si4project/Backup/syscall(5120).c @@ -11,6 +11,7 @@ #include #include #include +#include // Print a string to the system console. // The string is exactly 'len' characters long. @@ -55,11 +56,13 @@ sys_env_destroy(envid_t envid) if ((r = envid2env(envid, &e, 1)) < 0) return r; + if (e == curenv) cprintf("[%08x] exiting gracefully\n", curenv->env_id); else cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); return 0; } @@ -142,7 +145,23 @@ sys_env_set_trapframe(envid_t envid, struct Trapframe *tf) // LAB 5: Your code here. // Remember to check whether the user has supplied us with a good // address! - panic("sys_env_set_trapframe not implemented"); + struct Env *env; + int r; + if ( (r = envid2env(envid, &env, 1)) < 0) + return r; + + // 什么时候会出现没有权限访问的问题? + user_mem_assert(env, tf, sizeof(struct Trapframe), PTE_U); + // 直接整个结构体也是可以赋值的 + + // tf->tf_cs |= (GD_UT | 0x3); + tf->tf_eflags |= FL_IF; + // 普通进程无I/O权限 + tf->tf_eflags &= ~FL_IOPL_MASK; + + env->env_tf = *tf; + return 0; + } // Set the page fault upcall for 'envid' by modifying the corresponding struct @@ -414,6 +433,15 @@ sys_ipc_recv(void *dstva) return 0; } +// Return the current time. +static int +sys_time_msec(void) +{ + // LAB 6: Your code here. + return time_msec(); + // panic("sys_time_msec not implemented"); +} + // Dispatches to the correct kernel function, passing the arguments. int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) @@ -453,6 +481,12 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, return sys_ipc_recv( (void *) a1); case SYS_ipc_try_send: return sys_ipc_try_send((envid_t) a1, (uint32_t) a2, (void *) a3, (int) a4); + + case SYS_env_set_trapframe: + return sys_env_set_trapframe((envid_t) a1, (struct Trapframe *) a2); + case SYS_time_msec: + return sys_time_msec(); + case NSYSCALLS: return -E_INVAL; diff --git a/lab/lab6.si4project/Backup/syscall(6702).c b/lab/lab6.si4project/Backup/syscall(6702).c new file mode 100644 index 0000000..9cdaf89 --- /dev/null +++ b/lab/lab6.si4project/Backup/syscall(6702).c @@ -0,0 +1,139 @@ +// System call stubs. + +#include +#include + +static inline int32_t +syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) +{ + int32_t ret; + + // Generic system call: pass system call number in AX, + // up to five parameters in DX, CX, BX, DI, SI. + // Interrupt kernel with T_SYSCALL. + // + // The "volatile" tells the assembler not to optimize + // this instruction away just because we don't use the + // return value. + // + // The last clause tells the assembler that this can + // potentially change the condition codes and arbitrary + // memory locations. + + asm volatile("int %1\n" + : "=a" (ret) + : "i" (T_SYSCALL), + "a" (num), + "d" (a1), + "c" (a2), + "b" (a3), + "D" (a4), + "S" (a5) + : "cc", "memory"); + + if(check && ret > 0) + panic("syscall %d returned %d (> 0)", num, ret); + + return ret; +} + +void +sys_cputs(const char *s, size_t len) +{ + syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0); +} + +int +sys_cgetc(void) +{ + return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0); +} + +int +sys_env_destroy(envid_t envid) +{ + return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0); +} + +envid_t +sys_getenvid(void) +{ + return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0); +} + +void +sys_yield(void) +{ + syscall(SYS_yield, 0, 0, 0, 0, 0, 0); +} + +int +sys_page_alloc(envid_t envid, void *va, int perm) +{ + return syscall(SYS_page_alloc, 1, envid, (uint32_t) va, perm, 0, 0); +} + +int +sys_page_map(envid_t srcenv, void *srcva, envid_t dstenv, void *dstva, int perm) +{ + return syscall(SYS_page_map, 1, srcenv, (uint32_t) srcva, dstenv, (uint32_t) dstva, perm); +} + +int +sys_page_unmap(envid_t envid, void *va) +{ + return syscall(SYS_page_unmap, 1, envid, (uint32_t) va, 0, 0, 0); +} + +// sys_exofork is inlined in lib.h + +int +sys_env_set_status(envid_t envid, int status) +{ + return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0); +} + +int +sys_env_set_trapframe(envid_t envid, struct Trapframe *tf) +{ + return syscall(SYS_env_set_trapframe, 1, envid, (uint32_t) tf, 0, 0, 0); +} + +int +sys_env_set_pgfault_upcall(envid_t envid, void *upcall) +{ + return syscall(SYS_env_set_pgfault_upcall, 1, envid, (uint32_t) upcall, 0, 0, 0); +} + +int +sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, int perm) +{ + return syscall(SYS_ipc_try_send, 0, envid, value, (uint32_t) srcva, perm, 0); +} + +int +sys_ipc_recv(void *dstva) +{ + return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0); +} + +unsigned int +sys_time_msec(void) +{ + return (unsigned int) syscall(SYS_time_msec, 0, 0, 0, 0, 0, 0); +} + +int +sys_pkt_try_send(void *buf, size_t len) +{ + return syscall(SYS_pkt_try_send, 1, (uint32_t)buf, (uint32_t)len, 0, 0, 0); +} + + +int +sys_pkt_try_receive(void *rev_buf, size_t *len) +{ + // return 0; + return syscall(SYS_pkt_try_recv, 1, (uint32_t)rev_buf, (uint32_t)len, 0, 0, 0); +} + diff --git a/lab/Untitled Project.si4project/Backup/syscall(7911).c b/lab/lab6.si4project/Backup/syscall(6909).c similarity index 91% rename from lab/Untitled Project.si4project/Backup/syscall(7911).c rename to lab/lab6.si4project/Backup/syscall(6909).c index cf2eb2f..8a4d1b8 100644 --- a/lab/Untitled Project.si4project/Backup/syscall(7911).c +++ b/lab/lab6.si4project/Backup/syscall(6909).c @@ -11,6 +11,8 @@ #include #include #include +#include +#include // Print a string to the system console. // The string is exactly 'len' characters long. @@ -55,6 +57,13 @@ sys_env_destroy(envid_t envid) if ((r = envid2env(envid, &e, 1)) < 0) return r; + + + if (e == curenv) + cprintf("[%08x] exiting gracefully\n", curenv->env_id); + else + cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); return 0; } @@ -137,7 +146,23 @@ sys_env_set_trapframe(envid_t envid, struct Trapframe *tf) // LAB 5: Your code here. // Remember to check whether the user has supplied us with a good // address! - panic("sys_env_set_trapframe not implemented"); + struct Env *env; + int r; + if ( (r = envid2env(envid, &env, 1)) < 0) + return r; + + // 什么时候会出现没有权限访问的问题? + user_mem_assert(env, tf, sizeof(struct Trapframe), PTE_U); + // 直接整个结构体也是可以赋值的 + + // tf->tf_cs |= (GD_UT | 0x3); + tf->tf_eflags |= FL_IF; + // 普通进程无I/O权限 + tf->tf_eflags &= ~FL_IOPL_MASK; + + env->env_tf = *tf; + return 0; + } // Set the page fault upcall for 'envid' by modifying the corresponding struct @@ -409,6 +434,29 @@ sys_ipc_recv(void *dstva) return 0; } +// Return the current time. +static int +sys_time_msec(void) +{ + // LAB 6: Your code here. + return time_msec(); + // panic("sys_time_msec not implemented"); +} + +int +sys_pkt_try_send(void * buf, size_t len) +{ + user_mem_assert(curenv, buf, len, PTE_U); + return e1000_transmit(buf, len); +} + +int +sys_pkt_try_receive(void *rev_buf, size_t *len) +{ + return e1000_receive(rev_buf, len); +} + + // Dispatches to the correct kernel function, passing the arguments. int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) @@ -448,6 +496,16 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, return sys_ipc_recv( (void *) a1); case SYS_ipc_try_send: return sys_ipc_try_send((envid_t) a1, (uint32_t) a2, (void *) a3, (int) a4); + + case SYS_env_set_trapframe: + return sys_env_set_trapframe((envid_t) a1, (struct Trapframe *) a2); + case SYS_time_msec: + return sys_time_msec(); + case SYS_pkt_try_send: + return sys_pkt_try_send((void *) a1, (size_t) a2); + case SYS_pkt_try_recv: + return sys_pkt_try_receive((void *) a1, (size_t *) a2); + case NSYSCALLS: return -E_INVAL; diff --git a/lab/lab6.si4project/cache/parse/boot_main.c.sisc b/lab/lab6.si4project/cache/parse/boot_main.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..487cfbd4adf53c6c0c20deaf29caee4c858b04f3 GIT binary patch literal 7018 zcmeI0PiWjl6vuzvr0uGy8?9)Rs;g0Ht<_+yL>o&{s}W19O~u$M&7a*Qi%qgLsSTc5 z@lVwXA|48Q2#8Qcuot0s1wGb-QbeJ6(t}VBVh`fa?`LM-{$@8ZX+k3+K6v|P-psst z|K`p7wqr$->`s#Ab;2ZES}>olUD~A5-u@7k2ax7rO8NwFRC*8er=Wzqc$Gldg>^wH#a) z=rU3Ev0SpX@tQEzk#!PqlNOZ%b4u-vTKu)>OB(x1{O?5jM3G$wtr?H8%j*W~^-R;% zwNZ3iT^SqPf3Q?8cBygKROh|9ku81VV~_3QXxM}HYUn zybFDa`ORS-^adfmpv@QwL?1Z+`aq_j`ZM%BQ0^~Hlm{o3pw7^CRtccD3lT)y1gq)b z)LR32^fE}(mDY3)UFii6qc9`4hS5hwP3CQZ7n7;i2J&_w;G^*J<~42#`{f75$0tYo zD`O~(X^*=hpRowOPPjer*vgbV{z{`KRw}zVk3Iupg)b3r$c-e)eo^FN#Bkunq*or! zLgf|CId^%v`9uAc$x>zFc|5^U91E)tiz1kVFfR14%%wc-&Nm10aqN8P{GoE5u+Wh< z2sXF9O&wY9>&VCAQyk8UckZ3vTG+7n<$*lmWIpStBk*Ud3FbWwJH@O&UmamS20tl8 zcAT&3IxyZnNeDyW$9klxS>?Ui{K9ypgiwq|0RDjDMcW8oF%E92CE;0BA~;nr1P@z+83Y2@#qszdBI8Z`^4x-rOS1Q(jI}xjM2# z>xq%x$gX}u@hJ&tyt!pE(*x3%Ri4wYRwSE*NbVA13YHpAw|vd;wxa|2Oa|$#8#@3J zTZhIE6gO8#x}>eh`t`U06(0eZLY4sa4xM85b#CTkY`jna{Lb{G>>;qwy za{lF@D@5Fw7>Qx%vQ&krBH8G6pOF1F3(+1lUhK^Csu^*8NYkuFv1SdWJ}*QMMm{qw(H#an&mC^jx)nS7@@FcPzABNQ{hE zj6|N%qew$FNK-uOfjBN796vZV*`pTc9SL^^Dr;*};T0pPoiiQ?#M%Q=SBwu0O_ZHj zwxduHP$&fgWsRrtIt3XsnDM4BkCpaS`=8Y~`|TAneNc#_E)g0pK3Jz_RH=eydO+L4 zp^?h|aezY&fBW;_NQ>!Kcg|u(mC;tw)mF~FJ<<3mWwV`K_Oq!jVM__0_ zYFX6~KR!D%zGV(p#wIu3y>GJ7;3sq_f4ajTR5SRr3|?Ev48~YsDJy7fuq8Vbuw=g| z2A`C{y$uGpQY|BcIUhv%pE8&;XXjDPT6<9jBcLv&Wn6c-BN=lkEndDgi!g?Add*-f z_soxh?1{lL!{)~+7|(0Wx}x@<3OW&abULf7%U_edsX5}HT@Py3r8s>ctDFAFT&Pa0 zwX0x=j=*T#RuHaogW)H(an?^T6o>=RC&RkKUC5z6EbJgC?~T^p)XXOUW-JbiH&WLD znQ6d27iug{{wORoqsKzuC|zqW*l1pRM1rq%N-p$DA%+;Q7}fu&V~v-Unc|IVDowaM z1upBa542s>WWp;(GTZdOi&!<7ai{pJisf;95|S~*$C9yQugYRLApF^2NrpO0Rr4(_ z<}RbyW9_*sLY5uq>HZIBG4c7dn6FR-IoJq7r{~lUXb&)DHu5auwQf%hJMG9Dm9`nte zZ+t;2k}Kp8VEIIaKCI8n=^qCR@NEuXDDj-rn)B5!e$K5-6m)yyem8~o1A*!V>pm0d h@*NJqvub?Aar}>cp_C&YFJYkMiCb$|8K1=le*u&7_2U2l literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/fs_bc.c.sisc b/lab/lab6.si4project/cache/parse/fs_bc.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..e9dab00760759ea3ca7713d32339ae1999722d93 GIT binary patch literal 7180 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 DkL|`8 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/fs_fs.h.sisc b/lab/lab6.si4project/cache/parse/fs_fs.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..edda32cc8e8db88fbe6218c0503acf92d02591da GIT binary patch literal 20216 zcmeI4U2J7l6~|BKvp`EPV}T+lXBfnaAO!~~NK6L-2T-X_(SpW)-cBoHd((EX5KS85 z$KY3L;zJ)a4n&|Jn!cD!;>$dkI3_;S2X$gdY)nih@xji6Izi?7|LwEaIs2Y_=XUPs z?SpY8d)D6ToW1t`@Aa|wIp=nIc16)Z6zzDAO0+GCUjJwm{pP3JT=}gy<)t@%(}wJ& zpTF7;-yZ#+{#RB2+h3#7DL~&<`JT%4D%~nORG{Mr4tE^feXfmq;j$?DOS>xfAMWTW zOmrYKDtp@a!{6K9CLv9`Cf9Nld)^|>IOa>%H|*nYb?h^jM$y&s)kP_a)%mSUqUcpk z@EO&At#Z?D&8szvexZAwQ~eE4M%P}J?1XgvPI~^P!S7c2uF7K` z?3hOTh04vkKBPX|REE@Mz7p$Iqt`xu2qV(LKS55L0B$x zM?&uYLl1sv;LyWQK8RwL&-}%4ub6r*#eg-cQhfhM9(B6qoRQYwDd1)|zE@1WmSXNR zDwpL2o|y6O2Od83=tmFqGhvX*mmN9o^-`~;x9&=ymzDU0Ix{2zf=9qH44q8|ppYt8 zBD>0jcg`Ix6=x5r#+1+~*L7U8O`QT+)=v#lXvlIoYoT@5=+yk@M@pYjRnZV#p#s6* zuEJ!gqmoG*_1}5cr2uP}%USfJwqt6%I8>UQ87&TtPt84b`xBBeHFJLWJ5TQvFd0>#`GAOHne)%*)|-@s45V#ZzN0^NWS& zMq4Z6C1*ybeoPTFUnFF%Qn^6|g7>JPn|faN>2Kt9=R(Hmo+%Zl(R{VaJ{1UlPzBA@ z2@gVs5ujdat_+!4nhEny>28AVM!JdMkE)=VIvPXGgI^wP&2+KcB+rCc?n~XAU7SCu zCSmukX>!g$;F^ zHrJrZ&$YB?+E|oqB&NMvVKev4^caiHw)I{W2#)=jBX!ejb8aoQw3E(^sutTt3+ZoS zwpe6E-l=knza35#p^dt&h^mfGjkmNjJEN-PH*!R|DN9fo?lylj+*X9T&D>v(PHDBR z4zIN$^ToM&=7x!x8?^SJ!ySWHZ+Cs!g2q*rBx}9;u@3Gnz5i?*o=x&$K0F_YNq_cy zKpQfbrA}YNy4mvq_kY~?v7OyG*Q~xjnFb<9;T+0kLzIlWV{~Tb2onmy_C0X8A^6=gH?I+9X0NTYNVtR(|;W{3dgDi9<>qzM0G^>(zcRbd?@SFzC z8C<(bPSdZkLTCn2;uNmdkQGR&(rL_UiPLPKnx01zVvXc8t=A=3cWJOzm45I)m-!5- zh>gg6hB<}fsOczkQ9quSUx?4F`g_f9IAuV_!+OJh!?uB;P!@ZBvp7IZCV9Gf_RGM! zlv0iTZgx&PQgLkl6S|2tMa`X_-z#pak;Q@P!v4H!Qe8sm**T3?dWTlpz)II7R(e9$ z@W_&6D|-@;vF*rAA$xr zRw`S*Dgz8qCbLz5&Af2POny;-iIoZq3vjQu@PJUgrE(mr|7nnrbQ={ud8LZQAr`L{ zhnYtu8zBn>Z5D^GCAURyi4Uk){^|5`<20?lKdZXC!ugcTHgcAKh!^Hfe^NM{V;2}r z76>v^T&8E3DPy5wpXZzhO)IiE(7K`b1za<#n(6qQi)^ffPL3q-{VJvtSsdsr zz3&1ddyd|r-G^o4WB!0*j$V+g_p-#%d30yJK*wyF9oe8XyTBon34NWt<^m&oRp6Gr zqmxcZWj%`Ql>jhcp=JWMjRtV$GH}Rb^#L=xgq(3Wpj25`QGM*~N#14?0&GlF7c%Rj z1`9V6>>=q7{T6`uFJ$%lU~>m29>#{|nR4jNJ%V<^JF_LRe#zE zNtVgYl;cTN5?jRX-pwtEos{gkq30CjC6oSWv!ZMN zRN0Vh>6O2_M9s^l4#{|_W}d^uLW`;C$UKDx<5+t$&*6Rmo9A4EKx90uH{?0IaC%I= znGDKUIXJIl0b#N@^qlUz=g>PL0Bw7Egs$Nlu4%oxywiYVTxCg4v+Uo!*ykJip{!X> z6Rw{@g+m6lzSGo*JSH51?fbC6`lnoTn3BMrcs+BNg8J}|cdHx*dqT#;`b9eo%GiA2 ztRB`gSsXabTH_8A%V8FB4%357Z+Ka6KFPti%3%zaaSfe9{f`}HP0q36!M-NNWtD!( zhF{1W<_gJh8gm#`|En1$zc=yp4jB*Y4LJ-CP=p=ULKzMNr(ivk#eu{0Uzg7?xv=9C zF`dJ(E6F{U{X0*~G5wOAYx5hEzO*t42;S@*W5A3vuGJfMnLOB4faRXIPs(K$bb8}n zHl;n{)Hib(TNK^u?r&sMvV>}H5R9YC>J7U~9_+BdcIVFJCnf8WWeqH^?TC@B0LvJV z@vz=7SUfv)<3$1M^=>>UbYE6klw~LXIF!CJ?DTyMEEcPE+{m8tk~92;T6RGCURHrP zDBII^>?BcSoTk<51GkZrBM+Y}KBm5S9{sKCRlOx>OP(ImmKR6Ux^59(?o0^nEfY`Z zUuv<+EuQy^n?^57we)^5uQ59iqo>V%r1r;P934}|DhE7&P~4PzS*pD2nNeph-QuX~ zQ(tNH$WwF+*-et|W!=0||>(Ce!Q`7Hf>KsF}89Zwqk575-NR_96Xn zBM-J9u%|p&OgFBwB(TSPz6Pu_tmMrE%U;3UVij{6I25Z;uMajgx^utte*HMEDDL); zpteEvXc2~gFy_uQ00t=JTw9h)4Pr4qVwEMNlYhz=;CP%&Z`#;Z?ZUuk8{sA5OgSbpI5|}6jO>U4wNo`z0siCkQvT+ z)1*Ns*|7IRen7ZQqfoZO4{fQ8YuI%88uCMuKg$&QVL`wqa~Y@BdrAA_@jsQ)!=mYd5ni%4wn{mfDd;UH63NIvXeK_wn(OLAG<6*si@a2AverA zAAsDM5cpaq`t>h0GWLttzDbYM+ZJmr(cr=)OPB^KD#S2D|TD8wV03D&;40nEY!4wxW^^y`C$eZn5bwbT(SXk1KNRrtD4H{!)0~}xw$HChvt!m zVY?_#?tVvd{CY*hJ*)UUDOu^wV)~wnuMK=HB8&)Ij2(uInvSyNO;(0cq0M#zUfaB> zGQiEEe$9jY3i=xnh>VAIq3?7T{XgNHYCZedE`ToLA8vnZWwJQTg!*Fx-I7xE>l@nz z=uP&Avsv5Bk;Q?=<*(#5+8QPP6z&4(x?2T)pNi>376&>PzM0pVvwOuZfVKxz;E$-7 zR%CIYwM&1mz%`=_+y(j-CCs=Tgo?C{ovVQ$X%fCsP#5z zQDD1rV235^JD!0x`!+LE*vk=snHRz#lQjgk)-JH2q%i0ch3x`;vTXc929`C$X_^Vv zb^(A{c)%f()d!oUDU5TTU0_LYPkY;@yMSb?Kgr+%6UfbkYr6o@9#Ao^YFe)km)Ru* z+?{S0pcd`|YeII+8<#Wrmt@6tWWStDcLBbZUan%h0AU`}nP4sO%#g+bW%c@CcXF#E=9vT@vkpzW z@rZ)#J^uKmYaD|%lvUT4vbSHnG2$=7EbvsVSXSB)qZrxh(B3TYcv^#O8@VZIrhnQ< z(_}*2=`TB08FX3bGnXA~BX;IO0cS0^z4|g)9K^_A)vGU}At97jzZ&k-61Kj#GCCqT zOrOux{N<%Z2fW$QVW^BVcdOTTnB4MlCFyrNZQ0+e(ll9pf5~P*jW4YBd4m4r5qPWu29v3sCgzoq{9j|{ z7Lm^rbPj{AAdfldQ;fZ;h$UOz|D^L2{-BnHr{7k1i~%#QVNVKti2i7d^)w0xN-DZy&=H-?s33R1_f9RzE1^f zm@E$BV{$kjAG>vDLIB!*o?pza%R>ge*Vu>dl4NJ@OhSae-!^Z6H!G>Km?1X92m-6u z50NxgI0K&dL-$F6TkxL4B9E&KOLp+yEKJz9pv^qT7KVEp4HHYM_BG!)x~yK`Ys^m* zk0q%xhcN#w3D=2SN?4Put2e_6Mt+~vOstj?I5R`W!+L$JHB!Qj>fPx}wiJmWSP07P wzF7Bl{oh@MUk&q)>wkj4$QnSl7Mwhg?XU@1BDG&$3f{k_VN416-lo4gdfE literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/fs_fsformat.c.sisc b/lab/lab6.si4project/cache/parse/fs_fsformat.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..f75c3dcf6de0b2da10d99601d12ecd360b2a7c9c GIT binary patch literal 19067 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 delta 168 zcmZn>Y!Td`!X(4+MxLu9R+NJQ2(B?QFxWCNFg$oA&(#q7i)*q2lL*TSyZ2n1BbbDk zSTb%ZaZk=;F|kN^Ezji<2hp<#sOK{f8vxCDz{br1WHP*Xs>IC-G7tzrG{^`Ld-4P( xk9a)O?IFaO7 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 T#)052kKE@i;rP~oY4?8t1Or(9 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/inc_args.h.sisc b/lab/lab6.si4project/cache/parse/inc_args.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..70bc7262ff4b4f5fd6006b92304e7a8b7b4a0d54 GIT binary patch literal 5387 zcmeI0F>D+~6owbyIXRG>yAV4zh>(pEB-6P-!UBp!6gUQA#}1N#;^MQga27F_SGH`+}5dC=)LEv2aG-uQ6jPQIu8bQ<`bMb-p#z zY$^Zopn;?-_DLzgl-UD-Ev~`$^1yr5>IC4u0$!}QTd%9kSg;W6Q&Mm!lM5gB?xj3# zk9s%(w>NWMq5|yRXR)x(7;NaE+_uCxtcJ(jJFV5BH!}TngR3G;FbkdhP z0y@6UW+9IzQIyq~y}Q0Rw=mB(`p5C+=|=hIBknk!5#&G8D^rbf>G$b4%OlNNi6o({ zhU3W0KHXJ~C7aLD#bVy>IKlwV_H_r7M(3Z8q$xc!&jbOtOe+3*qR1{AcN~h#?=H-d zGJ*h9pQYGxCJ4rSp^H(TAXr_=b2tYo4$lHglmLW@Jtk|Q_@wD^ZMU}vT?Ul70DU&G z&&wJpzR(B#Fn=I6uJk;E{e8bN*(m>*T*F)&bJ8_&;m*K#nKW_dU=iThcglXAsUy~p zGV}b`eW(9KGRC&RENxcSUOz$I#9u=c*=%m!!op%JAM1o-y%Rc)!!=_43*Pr;v4TPz zI~l8;TH*?&W>rkfTgS@koUq>F?AtTVe6XQ{oj&A(<=!UPK)#Z2V6D&=qAOqtf2eMhux0NB$Bm&$;E{|BqcWUh2C{Ay_COW?vY3U{6r2b zX@kk9z?ie7Pr*>x9TQ#)ECj*s)Rkb&vEa-yua)l$b8f<%=bhd7O}g*e`2|o?Af(7b6Ba9^lmWf0Q$HTdrZpCn=kaJ)BdzW>MSX1)5#e$>a5}0 z@2JCra5t%A!I@ROX~6e|Ix7n9;~aG=@~vFTsKaxJP1!B#93@JLS-;#dbpSh1xULMzyfZtF$ zKaf_Gg!b)oWwOinik~P2sZoan_}>XtW9jt8&EWW&)r8kow%g}rm6;pMnpuze_N(lR zzn%>*ZhIWF@K>b#9Kt>-1=M_bP&K8p8R+tqn#s(oQhst``6>cxzC0*xCQ(FuPQckS zd!ebtk_jX!i;}}a^3bN%6ujOoAS#JWdX>N{v9&< zn|p8TQ)_P5_~7roGO$ISy5d~34~Xptr)OBXKKn;C-aYn%Ha6Dpb6kDy{^|ORsSV+! MhxjC5JObJO1x!RXrT_o{ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/inc_assert.h.sisc b/lab/lab6.si4project/cache/parse/inc_assert.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..f0bfd1a59e4a3bad6da29f139675cf5a635acd4c GIT binary patch literal 4137 zcmeH~J&06S6vwaYsH^T~)~~FB;!9$oO|(d{1QA36F@?MCC`cg8&hG58!^$}9PILk( zf}mI`T1;V)M$nML6oRFNpmr8&Qdn4uY0`*x&;Refd*;1)uI{S1Mex9xbKkk={?9!h z_r5oKRyudYxxy~7>v3*Z(Yc2=dOSapgs(h)*oW@Q?Q8wW|?piAP* z;x&N_l29}6(&vr2k2*VF%aa52U(Y&8g+PDC;whWgLjL13wIx z$_Eb}DDN+oz8gJK{(f9a`Ex6Rk(a>%W1N2$=H?S|ys4wc!KPY$;`q%51%)d5UVL$lYR48Rt=t zHO701)hLeB)Umsjef?OQ|R1Wk?BQpmC9Hv(l_hloRx-5LSyVXBH-#M2F0`Tv~q4;h-!MV(nmUs$QL(it?>Za;Vpa#ME&o zK;WaB&ks7I*V<#UUad_QpZjZL=-2UH)(|r5=DSX$ z_GFkmum*+}x=xPD<_~Y_9PLXO*|Qd=I=XLMCmVjM`^rdnf&aqE6SXR!)CN#G0QtGt z9HEPn#?Z*QRx95TInase$}FIG0PL$EcS;&4y2ve~+dQ>SF9MXs+47926i-8WNK;k( z-{G0!KmEf+BI2sab|U4Gy6d**88SP@_+j(VLgm4RU@b7qAQG~ysw`U*P4fHJvXH@i zu&y*yoDQ!x3--Q(y}C9HmQCB{mm*lJEE2M6zjLr`JX_g4s_fkmtR1{tRQp1D@aQ_O z-K9Ku)~_bz-rtzW_da-ltq(w4e^k(PQHt+|W*2cr?BD^gBLL}(xdVzW${f7SzbpbY z--`|PVmsH11Jd;lCcT(_8&lek-HVnjlXBU~;5+wXKG@$C*sUkMEkQcuWassj>vf8;?^C*}-F+z0P=sKIBS0r1kjYV}mbu zq`p$RbdjF3l-)xdvpOMPoEFd7E2g1HH-C|fRJwD0X{4mpUH~7fhb>ZG7`%3ob_xeq{;S7qAVZof9Kg8!BoM> zQqh#eG5HE}0PBo~Bo2njjVv;gU0KXoA_{=Q4@G1*&t)-Xu?PTaUBL@6&J<|ed7yC( zKrx4hKX@2{R#?;|aWI4I0s;Xb1{n{sd-4P(k^T zpz|2`kxT(v1~Fwm2hZktLd+~22Y{wz^s?}7UMZ%)$dLf_$B)Px# delta 332 zcmX?UaMECd3X^oeRe3H6FKI3YAh^ZKz_6B`fnmZGd9D=)xhC5)iLx|kz318-!BoM> zA|T4fJ^2cA0P6(_Hg1N=jVv;gU0KXo3gm#o4@G1*&t)-Xv0%6+&(+`qG0qfd+>3^JYvNKc-?B(hnKb0@3C2cYT`YehLgDt0pgRRNV3 z03CE;rzG427N8s-5W`FWsRfxZ*@KT~vp&B#vxNfC9T`wls(~h`0JRtZ#SD7c;idqc z$H0$d3eYl$Df2maHqR4cX5kP3y5)iTTdvJ3#S|Dh7J!0kwG#K_i{dgI7l53KjX=&< G@elxC!bem9 diff --git a/lab/lab6.si4project/cache/parse/inc_env.h.sisc b/lab/lab6.si4project/cache/parse/inc_env.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..bc1f86d966500737775fb45784e09fc8750d03ba GIT binary patch literal 5477 zcmeI0O>9(E6vv+}w0ulyMW7Hh85PCKM-lKNMu`GLfsg?@1q)G!p)&)MPKU{KW+E{r zE_CBoU6{DQ1tz*;<33{cy0wg6F-#8H~(~fd9*fd zEhig7eV;UOY%kf^_?WP(Q+pNnJ?+&GG;q80$08il;NPXsDA7-4|0T4Rd*1iLu<)b$ zji~=MF}x9y-O}>`jrm$*&d5g|mV?tnk&nrVqC?{O`ToFM8)FBK9LwboXZPnb*;DyL z=j2f!Pl-5jI%LNt@|vKj>tEleK7qi#zEYd2wu+_ni!w%=m+Gb2o(p>mYN6HgbUKWJ zy>t{-J`i};bw+6v>Iza;1Dio>;Kb=L2=>xJw8cUp$fk}UGo_ho;nKxcQ<1bCUBOm{ zz0t!05wI0>Zd=NnC%$hv24(3l)!LO}zIjqTDaV812&cmtV0T;Uu&YzPAMJEzu;S>k zv0YiM^9el=@*&qP)FCmxZp5}D7~=aBHV}xH87TuF4jY^fNwI08A^@rHzMXOlA3V+{ zRicxWfD%3;Fwk_|cE?t2CbXH%D*?Ow;CLo;N=Mi#ZAFHelnYVW#7@vBo1M-0HNu)9Sv6pNwik19H|8Y z`D!Cq;;-wOyWw#GKGShK$=$gt*yw@4Jp_05*hEM2TpsD5kN1Q$D zZxw16Yxz>GHS;#t;-K)90H^7=olHKu2q{eTMDhb9KXLkaCVw)Q8K>-|wN-%8blgs| zM`wD0vOD&JIk@i^wuRiL;Wqo;xU)HKrnSO|z*21&;KepyJkO!g+o|ZzAn&M^s`O`S z?Fn7t$7SoA%NxZfj0x~^W1261T88%APSI}S`>+Lq)hphDG}pR~vmT7YhhF^ElYA+Z zYsFGI-SK|QiG(-QVF5W3C`XR3TabA1eLuRgNU*O|D^|*BhH;zNZvA$AdT?^2*KH|o znxZhI50c|wObybvz9Xo!eL!^k_)s?|Ru3K+Yjv@)@t3?sE5L!ivxK9nI|dt-)+Jh=Eo#R-6VGMoyMQ&ZJHRyTW+&Oh#GLAS1;_3JX1q;+ z&ve|rH|}hX+mRDu6;889*90HPXSK}l>L6d0um0w@LaNl9808Im$fl6xdzpjY8bVy&9Kz z?p=;@JKrqx#liAEEpTMeOdneFC27?(C-c5IbY$|?N^P2asmm^5WvHnY#O7O^c;401 z&R2@O_*js;uRv?Q#cACwrQSh$1LRvTG)lFm*r^6rz#f6i7j5k3OR`fvldjd^ zX8^0qe4Y`wOVI32(l%d`m}#2$KBEGms;fwQwpnPlX33eC(zCcj%?5$md`W63OuFXW zXw_!PnYZI}0!2aF3Yu?m;x0K))m!?0IVCDq?*)MnXsF29d`W6G&B?saQMZcciDshwww>(=x33rqoRd&PW7{?1LKR00$={u)t`mjro%7o#kidfl!;2)u!CII0HGQJ19_4G-RZ1z9cy$ zCceJ_dA-z_*{ohv8N>%cZa5+vSjNBTA1v3e4@Biwo32x9c2ED}pSy{>P@jeT|8V*} zu9nmov3j$ZXZ2Ne=T_c*j#F>iNUEcc-q#my27N}=h8zl%dx?#N_Kr|vc3y0Q3X_aMa5ldO&kr63AW&y#U=ZeHU|0YYD|nf~KiPpvgyqHU?>w6$n0~W! oEC|ZxmpIG9J2{$b4u=3x&5zgd9FzZZ$#7f%%49qOa+JAs0DUhTf&c&j delta 89 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/inc_malloc.h.sisc b/lab/lab6.si4project/cache/parse/inc_malloc.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9c0aaba3bec48ef4bd5f61f13e228d00fb745f77 GIT binary patch literal 2471 zcmeH|y=xR<5XN7VOFSfTQ>ckxafL;Kf+&zl3BZG3n$i0pO$>0j9ZcYjd4PXoOauZ#P{d&D_0^!h|CSUcJ~ zi2iApbH{{}qjGl%j;zwtf$A%A;8o;z@Axq%^*cVaZ~%F^Q`ecNFkm+8}|V zIq@|ykI7Lihte~evwBiJW%EaooD-stp|ho#@|p9+^0}$$=~A(LdPavdZcQS!EMEwn zne!oI+c8eFZvC*;k z#l0$9D1NRNcXp%{P#nA>A!Mg5njgN_Bzm_k;=_AnF*dBNi^0(+oI51Zq}B}@+Tum^ zGi>=bEoH~Osc|Qd#N!%h8XEWKixf1JqZL5dXg?1vGPExJqz#i|$k{*dC3y$TXq&0YEwsgNCn>fdScR zi{>Y{CIHa8Z4n=P?QAfU*i7#X8oXlS_WTA9OJQTMU8z*x9E*VYqQMqk5QG-jxBV|o v?};7DBTGF#mb{SF0A^qG`~3KQA>f?+*i541)0nCSjjyqf9zEd$@zwqVFs%3R literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/cache/parse/inc_memlayout.h.sisc b/lab/lab6.si4project/cache/parse/inc_memlayout.h.sisc similarity index 74% rename from lab/Untitled Project.si4project/cache/parse/inc_memlayout.h.sisc rename to lab/lab6.si4project/cache/parse/inc_memlayout.h.sisc index 49ccd6a9800f855ef6a88d1c0f8f2d9cacb5b6f4..ca71cfd5260f66a9b3fc188eda19d9a3306ec8d2 100644 GIT binary patch delta 933 zcmcbodrxP@F#J|OuoP&vw1ro3$uxVXEwivJ2Njx z@p>R;V`N}Z0E#vAvG4+!3@ZeaI23`jBoKoP2HCXvy`VTFD@bPh1nzlXwDC7Q4Wwf>w%aJs65~eP)y@3 z7f7eZT_tWsAT0^Rnm`OPcJq5daYj}khk;=-x3E0N2B5ts7IJY+HWJp@=(UY;VnB)* z6AM@#g@NdN!< diff --git a/lab/Untitled Project.si4project/cache/parse/inc_mmu.h.sisc b/lab/lab6.si4project/cache/parse/inc_mmu.h.sisc similarity index 71% rename from lab/Untitled Project.si4project/cache/parse/inc_mmu.h.sisc rename to lab/lab6.si4project/cache/parse/inc_mmu.h.sisc index f643bad89061cf6d39dbb8fc3e9d7a34f5a9c6f0..6d250c09aefdb0d637cf6c6eb55682972f0a1390 100644 GIT binary patch delta 1271 zcmaJ>OH30{6rGuN%0yeI5+juiSYlB=6$(trplFC9HWu4Lp=oTQrC%_hOVn96Sy)v+^UA+Ckg94(Sti@S7M|AbP<;$vrqL)=wpk+M=r z8@Z3{vQSL!me#XLY`IKo8U^yD^fc?gr$iGvLAD=%#)y;WdwEBN#-OV(OPZ zeI_l)xqushawe%BNX#n9W7UPUJ&Nt-{G=YM)G#LZ9z6%qn{hh1BjLbW8<3XJq--Q? zG;3c1J)(}X9VPz)G31s$`?+)nPEg%hbefd7++;v9$>a4`XuA=ov8I|xOPG-}t!E@! zLHFcJ$OF<%S`4F_BX8TlJJ=Y+4UMOH30{6rGuNYMlW^qM^V*VJuPy6UZZ=AY^InA)<*CJ1mH*6`P*#=|y zgUFLxT_vOsPV7a>xdA8e3{I5oVePXbZy@Ds1=VZ1Z(@Vs=byI;YF%!04eJ7BCz)sx zNWE~$7a?(mW?fN{-<80BmNc;TS0dj=>Z~Pdd+DIb7=|LbW$R~M??rwew1~Yz zF$UiOofb;T$%;lc{8r>UNL{Nyo>#Q6F{pKrb0$G`Y`VtC5-KydY%pupdSOE_C$OnU z#XODo*i(^b@Vl6LwBkv`Z6Q)?4w0&e?Ehrteq{-a0;BF`1PuQdP>6T4Po4rH(7HT} zIIyXE84=@3B0IMDWD0`|Zd)#r2h6X_gk4Fbs@mr$`)cKGr!tvGux5U3`p*1xKTKI7 zG?{T1sFCX3v@C%>+nmgy7hzgEM?oKbkSU}ni>_;@M6T8hImWe_C`pk8Ef|&uVgKp^ z^U_S(fOW!-z_ghpav+gvMLknnL_4H8E9degm-rMq_XIgRfjO@irps`F+O3l?B+7_O zH0yBUwpe2_gC1P@7ZyTRp+8(Y12?E$Iyz0tJG;oRVo}dF?xh_Oge$2cmL$f2g(*HQ z(GI#NTS6UHj?(ftl2v+*JWi>{ITSULOHpxPW#w^hsR#RUc34MO*EQeCb;NOrB{hOV zY~E#U`@oc{OzF}G0M+Q2iPDLW&rvtJjvM3wga=a+LEN?g8v_<$GPw_e-mjBTBW{6_ zLx{4ZcCZ_)-!Kob@%c>S4uI9CXO?w7q-8&%E~dVtNt~KACdLr0oJ$vc5cqZ-GqE{~ z^*L&jr+_w>q44;Ol_w+KO4Z-}ohCPXbJY{xkBm8lZ^<=*{{(-(V2;9xUx9O>|HnV3 CSZIU* diff --git a/lab/lab6.si4project/cache/parse/inc_ns.h.sisc b/lab/lab6.si4project/cache/parse/inc_ns.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..33308dd4d6ecd97d4560e0f1f83ab1e79fae2335 GIT binary patch literal 13101 zcmeI2OKhB1700KJlQ_vZc9S#>6d@xDb^3~%zFhhsspIC+bnMtpYg%ADo*6$9XFQ3W zwiF=+p-Qa;C<`P4Lc*I$MF<*IEw5E25bA=35i06}MJOOuNQiiWs8}%kf8YJie0T1Q zXH3#;5Jx&W_dDOc|9f8dF`v`7A(a|SrP_CkQ*Ei#Z}+59@4VR-@$V(!SN`x$8r>_u z`avghUF!ezzY+m*{}J&HIrJ^@_ry1dd&TWy=+dLpT{D$`rfL6hV=DFU&1y*3{Zpqy z4^?7Xde7;3bZ=6WRzy2vd7UKlSS{IDe@xsp5@VY8vTF60disL+76F_GMD$(h-&cE3 zlT^gd2z2-QRO;uFFG}u|Bd5hndiNX2_oRD7V?L*`K$s1Y>`rtYdZ%tRXf%5`Gmy=UW)DqDq4`ppE22oo;Ix@|t9#Y(-%5q>I0F2J*;0P)Y@yimkQ#dy zzf>+Rw9Py+p*OrWGb0(#Lc8@W+ttevINNJxId$r+<|s-}DJTOGMKYd)cI!EI#Uq`q zyQ*`{JvUq4voN1OSzN>!)nXK`O_Bf;UBH>#bcEDsxpyV)KUXSdqMK;riP@=a`Q##b zCK%hq;FNhZ}nL@QRI{37n@r&gSo=Lym9J^Da9D&^#V-ICAg9BqEl(}ydgJ{xkhv)>- zXqAK>Jv=%#m?5DI)~*?o zGf90y44z599b(spJ9+CFK6HFc;23_r_~sbgB;eLghD#}`jY{(5mH612JM`44r==gYa$d?{NjojvuH5shFYxkb#1o)&|>Uu-(;F}iruN*X_n zDwSl5q2KzUr}vs0x)ajM5Z_C}>=BbjyG zQYT^wHo8W0#9bm$sU$lXb8IM<3bRu^n8!-sD1a^vOn3F)xh^WQ*Yvyr+ym2H=f2tA zNwZH!1E#~6)96Yfbwm~h9+=0TTU|+5%@8g#C-yNPlCtr2esc6mp-iqubuOPMXXlC~ zPD9KAikKj&A7D)v!s=C(>QBRRwvb!Q?NSf_cGst{z1sdkv<$M^CBG*7C#2i=i@7cf zC3#mZS3}9Xz6*Mci2c!sdDa?AZgkgcvR{k$XwaX;7vfx>lXVg@-4S2#TisH(iHR*!n2u+ozE7^CegEor z$AvS+)7e};Uo0<@e+C1J!$@{0CN!O&sNs*T)a*HR`FF6`4BQ=JZ0-}A&8G8hzStAO zCbNK(&dBD)*ybmtzj7@BB@G-o36&_2UgPbGpbZ@*%A77K)GZA(l5r| z2gTS&SJV0ScHa|5BH1zl-Ab5GXBTG9E*9pWD;<~0V0jJv7BM!{M(5kSyw`19Qb$y& zFxnA)I}CVS59Husl;Y2dv6D`w^X3oxi?_Eg~H=~9oYunIu zhY;@&1BrP^3$f{ZlP_vVN@|Q~B=yJmc0eg(E<&8N*qak$AB)d)zP&$tX|jX38Ld*r z?#DZ2yi*J)Ga{{wP3K#DS;45yUr@#lmU_BToEH;2Br(4(Zlm*I^%(hxgenz`aYSpK zz6B6mZYEP=CPm^Wn5GLc{pY&iX3Mz(fupD_?w56anu%|xuS>s0=*zmXPJFe!lfGQn znGgdo=33lNDMDyO%v`(0%!jlC$aKDgdac0uw^K^Q5kXuZoo<{ycyg^X!Z#Y})Lc#?$dvhHVnOdTT%vF@kW6x%0qcH}+?!iq4>^sG*7^Ho`FkP5(AG7Wz6WvCY za;v310;HCZR!gP}fz_o-4Qff(+{U%kt$=>eTU$%R3TW@nq?X7hj#@yO$ajQi=)wN$CuFjZGm zrCbasfxTjOy!*u9Q6{EqUZoVEBYf`mcRYT40p{1}MLB*bcKpYBw_dZKlj9vPC;J`s z$%VX?<5rXE8X0ZR)Q}PHLZU_?tIr42CfG=;Pt%172Up!9YFwXVdWYAa5rc}v8^Ub5 z5LK_LRQFYfGMbrxj`4mZ_i3Z+k57)G-hEpgkAiclae_zlB5R&p)Oe{dTv@lTOypgaYzzXR^z4KgFO{ zWpp*pTM0d)c}OowrJB?{4Sqq-Ma|GK=QM#9K;(@Lfx9zbJfcv#$?) zDIj{xYr5vCuH7N8_fs8xK>*1w@@3`u-FUmOBP}Ie#~+g;4NP)vo@1dOV?9PhK^id+ z4AIxfX=?wazQQBzQR&Dx((-M(|32S#qzwpdn;2cNHbk? z;M#;WJJR;4Ajjh(EodHiSxfSzbW)_)&@Q%Aso5}96qOO_Tx+TrGa+Woelg2`Pz)YN zn(3MsDZ8~J=G|UXr0$aucz&5K$5$r#DAG#QbYaeY%*LnMxKQx=uozS%C`8(HA*x6+tmjyM8m6IZXPx!m*u z2!5Wg`H4J3bTeF{@RcuBJro{(e;RP@=&XU{6cb}!)fZo94Gkx7Nf_ANR$!t zthM?X^M)8_sc!K31%}f|wpd>mCfv8B?vGKFQepm7ZZ`V+HL>Kj12odg-*h3gx>TuQ z`SZV5DRF$A5Ce>4tHfs7=t5xL8HHcd)7V@-Gru^WpP!?npRfrf?1Ol_%Ih7K+M_$; z1@Aysg+#x8EMaUsI_$F?eh|rfXiQP40~Dk7hRuibV&n6B`i{;}dGb zR(r$yb!JaRtB#4+(zj8cwT`796k>$Wu9y3_ z**m{q+-{3UxP0EHP4As?ZboLXG#BgfB7%2kCn643X9wBx!k%T`rHz= zQmL6((!V!u-yTc)?Ty>_d%B0?_C8OyKW;zf=?=#2$35NAxP8piv8h9!cOtT{KW;zd i=~zT>0xjid6#P{Z2Te4xm}p{*r4*@Rsqhd`lL!4lsc8zab#QZYabR#@ zaFoHxN&f-|<01ih57m!C3+*3!~p^d4x&kzSIu-ZjQmt2M+u1!Roz z4HiBG$#LK{Y)=qi4ETia+n8$s=3!?Xf}(Y2)wT~dZpAC!_)7Iura)))T%MS6;)$q} zj7-{bJ3f(c#!|3zZv;ha6N6*Mls$94R+Q?im>8okUrZP7X0qY)7@RKOTgjGuOBZfn zhO@YwTC_G%IIg!7h`+37>_5W zXDdN?33!nE0hZ$k!0SaKv3c7vIkX+v8AO`T4H8T)+$rbF`Q>6JTT17^Qf)EwSeOV< zbuY3vno4$}T!;}?&Ta>Iob;y?Uv08^UqF0n;c<&F4*e*N zUTykJ1AYX20o($f;rklE91Z|%O={-gwaIORS&KI>rDT_GF8Rb*GXeAjKvau){yl2Y@= zbD`L3Nq06}wJJ=mh3TRbcns{1Lje1MT6o3wYu=h;D5MsgQWO0&^!i-H#eDVE^}&=D`cn85@O`5kb(LQK6FDpMmH+?% literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/cache/parse/inc_stab.h.sisc b/lab/lab6.si4project/cache/parse/inc_stab.h.sisc similarity index 86% rename from lab/Untitled Project.si4project/cache/parse/inc_stab.h.sisc rename to lab/lab6.si4project/cache/parse/inc_stab.h.sisc index e073ebfca0ef8fe832622a5b6b3d5aef2ad62ab5..2d59090ae92c962be776f89b3623a70aad99c404 100644 GIT binary patch delta 168 zcmZ3ZyheG03X@ENb2`7ozwbN@K(L;Rfgzihf#HBlI{%LKUwI}wFp03dxc!}Ha|F{| zE|vh9k35rq^1QMTa82j$Fl7d+WMJ6J$iUFg#K52c6sx$E!w=*#2q=8ykp@UM2HSjuDjM_yFWEyaaLr`9&sA H5tIM`zM(It delta 168 zcmZ3ZyheG03X{x%SMppJB3QT?fM7ir14A}11B1b9c`gOT4_uQSm_%4MIKAiE9Kke~ zi{->~CGN>Td0ts80P4BnAj$#Kvz3v7p`VF?VFOT1LSLQ>$Ypr(REb*_NXr4SJP?D7 zU;)yTCoqX@*5@;2;xKq4&*kCD$2B=e;04D9pmiVS1I>;Rl;IEnawJv*If48llcxwu F008zeEO!6^ diff --git a/lab/lab6.si4project/cache/parse/inc_stdarg.h.sisc b/lab/lab6.si4project/cache/parse/inc_stdarg.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..8c3bc9a13ef20415b2d8919d401a0d5f138b3416 GIT binary patch literal 2442 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 OE<|GZbEzW**?$3d;Rba8 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/inc_string.h.sisc b/lab/lab6.si4project/cache/parse/inc_string.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9af227c51cee15a5c95b64e64cd7f49921eca803 GIT binary patch literal 19591 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 F1OV}dP#pjO delta 319 zcmbPcGtFj$DwFhUc`gQD7H$S0SkA-1(8J5XU;q@m!KuJK*?~!f<%jiquFVll2HY$s zZYXh2J})Rcc_ps}YsWJsZqCVqVltDN_$*m8o-1*0w&&Z(Xadw~5hKb0GVUM~1H)Wq z1_p;W@?00p*|-^jVjd5axaEK}&=v+2AO@MvFxgI0e6zKHCbMb7D|s#nFKI5I48u(p z1_moO28IPdu^&IA;M(PZa(qAx)6NCfzCk#W)no%uX$I7GNucc@`~WECAp4mMX1fAV zt1^=I&9;(TnK%T1ZVY%T#xZ%GjQGTW$&;7J+~Z&XihY<5^mCW242J@cBe5FDDPR$q Id`LC~048=#r~m)} diff --git a/lab/Untitled Project.si4project/cache/parse/inc_types.h.sisc b/lab/lab6.si4project/cache/parse/inc_types.h.sisc similarity index 52% rename from lab/Untitled Project.si4project/cache/parse/inc_types.h.sisc rename to lab/lab6.si4project/cache/parse/inc_types.h.sisc index 32be6e8b339bfddc2c156f934df3e3e549933e2b..6bc3a7f9685cbe5a0c3d45eca719d37d9b251828 100644 GIT binary patch delta 549 zcmX@0cR+803X{wS=XCxZ3_o}nfM6FF1H(0L1_l9_bbg0jIsB6ym_%3}T>s9qIfBW8 zgH^!tBM;}~mt3-wH*p!UOfdY&Gx-Hq0LuqcAj5_`j8#DYBM;x?gRC->H*=e_TrdI( zzvB*P-C**OhhcIdo6P1ko(@LS4?x2$LYa9%7A^zgpFq3;DCRNmD-R=(U10o?M+`{w z0kJ#~gY0LVEGXhXF@RN27XV#-BLvlu2B0AZvk``f12uze!D7g66QJ4* zs3VpD)qDfu2|%$KM-%vAj*tL~iD1+JOJIlT0ia@yFo*+S1{k;k4OoLPKoY1~6q^B7 zLOV<@fNX=hAPA@^1&A*IeWk(g4|jnSQ0zYxfP!JNfQZE81FSrgy@V&3H~?)s0X1L^ zP$3i0-2tFrX=CAqc|jV@fXxCT7K|JMK%Fbj^73vD5cOna2fBmdWitQd1!6KB4}khF PJOy$XSwtq^5X%4nGFzPD delta 549 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_console.c.sisc b/lab/lab6.si4project/cache/parse/kern_console.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..d2e69a4739b600f6c895383e7388af013f499052 GIT binary patch literal 23244 zcmeI4Yj9oFb;pk%7#UjB6gGFijY+Gp*3{(G&p*M6LHkExmwMNLsOeW^H_8b$BbMbTS7p6d8*N%#lv zyj70w2S0gs7IIqjJNj*j0Aqiyc&;4!ruci}_2PQ*bTPE6VcYBjRV`(d-_j;_qr9ixZ+}?6TuLsjPO0VS3tmoIlf3twD7scg_Ne?#$(*j=6#ujAS*rfunH@#@WW)Qa+cPJM zE>(QKC+A+5)g9uMI4YW^0Q_9sBYseAP8a?Q3VOZxv%*>~obP@rir$rPD~0pLGo$G5 z72Pw$&#Jsd@*%bPmFhlFyBhJ|2xGIb>B8dcq%cf}l$k{o#yFps@t)*Dbar=dd(V!J z&fIk>Ts~BN)zq%*w@42z^^lXk56W46xQ6~uKCcY(MEi4#`nap}uAbI=w(l5JPwlFu zg$BWqlfEa)WA~(C@&wMjqMo+3b@X>`_1yrcJaOct?}qZn>tyV)g>qIe z7GZA4=QTY&(aSl-z4UdrclUoq{oJZLtY!(rD`ce@e9EvUrn{Di=HJs+R@{%pA`s+rkq9|`r|emJJsSr7INQ?!S)8#YB7iu4u_1VpmMtwh7xM- zYb&GPD4$RAk=UHp(W$w2vr5=>o^R%)k2qz}L(geeoTqM{qK;>8ZQV^*H0lCpFF-4D z(t}dY0(A}jpL{;)5>sagw56dLSk|-8`esggSjt(j#SzGWZE9}Pty*0p?QD*m^stn( zV2eY*K*xl&r(^BfjT^xgOq3HtIsSND%2~L@=4RsF1TF)R5VgbXhhhiN&aEq6v4BYj#4wnV%Vvp;j$EBQwi_b=x_HL|gxLplc*cbbT zPI_F*Susv5Bo0j0nCP#Z7z-b?A{YQ>{xo&In6Au9F^@a+cNTj559Hw~jh!)+VAtK!bS1%N+qgswIqAVE zX9YWfJ4vuNHaeqOfiDGAaY&M@Wa@z4*su=MbK)z+G((bJ=-Q^-IJLirL|j^Tn8Gx-_z?3RJS|wD%9TwC&geZl(AkF%~23_-MK` zZgP;$SWUp~+de>uS)zA~3GvfnY-L&ex;RZoG<^7Dl+P35WcbS3x3=mO9gp2Iwc0NR z_$@KORpMvFMmBaskEYMMWjf!35a*l&X6#5b7Uy43BXUw90f;igJwqJQ)ZikYPp%ld zshjFJmtP`&$2W7*LsHIyyx6xz#OLx@0pw|oH*Q$pRId^X^1E?GEIlf^EL0pb%ICp~ ziPE&%rj1~-4*xP%$I@e>%fe(RjY=?AZ)_q3SbdNBMoxN6%2}8;YTQB!7-45w?dtl* zjditOBxkF{|KV$$^uUy}fRnMw$k|P|+yw4zx|h}cKRvFK9+z?!E;(+L&$H3Qgu!Pt z);82_+z2$8aHaUYxHgs^8eJB2oTKp#C7H0UYF%S3%MBaHuf(&(?~92bi-_sclXukR zW}vHk%V7KVj!r_cLOdb{`2#V?OaRlRk@3%{Bou?&w{Gp}1RA4Pia!(seTHy>o*U@W z&^T#S@)+E^z5lM(p1WH6NVu!S<$?qG!T{NHY2@M{rxMPEL%^E^cC~l`cAE5~hmPB* z^3~Dahug}Co>4w;HSwA=rM6L3)#62}z|<=xX(P~rE^TUY3MWeusWzj1&?3MIVui4P zy2iuO*mP;w;)F;=puMZLpF!ggbglRX0kZ0T#RD@iH7cj6`GNFXky%Bn?kv4@Ybl&j z7&y)+10HFoN5|B`4D_nXgeMV5mo@9h{Au6``%vhtQ{$Rj);DgrV;ytvdhx@;!O+Jg z8GFnyovs;QM~4-S;4~)dwm;-r5--)YhokD?%a0f9sJczgj>}Y#ymPUVeeWO2&&&k{Geg zb_A?di-R8JA3ZYuy;eX@p!%whmoG>z9r6@sQ=AzVjrioVRxMxmC_neeSn)=HY{lUf z>1d>8Njvsxf;S0Zo%l_E=Q|#lZR9OOQ`u&3tH z82{yQ$+&+HaIJXw>f`0b>oG0kj01N@_W=3ETp*g3ibrqWHoNP>Y3>2>IqCD_NWVjE ze(D2O{b*>YtqfOK=rD)hp(JE>@;emQ5ZxHjk6qK&$m&DMLIW|u=#vh}C6g)^%Gw!7 z-~xeb7bO$y?(N+jfMN__=X<$C(mFzyi`N{LqG<1~^owH)RqW7o7G7=+qnK;lHoGzx zN0Ef+5O0?5z?pTk>74i6NC-Jv6s;X?>1VuAq>rgHk?2wHD(ztgl2 zU{C1lu(G3TVeIQOs{4&NC*8;sNygBAf%sHq4Czb8m{hS)o^Zx+V8Lm?KI0`@1cPC^ zT#zx<{ppOMWlV%~N=dp5Ytl7eP@FD%6vFCH#p%M27nTOd$xavZ$|Sa1d7|lp?_s(e zlwXJZ0>eaSYK+MLz4Pj3)0PE>?YvSvR=QYB)O5mA!xk5Iep1CGSXQ2Js-&GgENA!n zxzQwGobA&(uy=Oi>;maHMogx&whnN@#95On7RsYLTe=RUeLX5)k1U9j*ami=e4SsO z__|R2aT=RUUu|GH14Aam^60*%7I=0I+%L*vW$ZD(!2eoxEP5jfz_C9~7QIE%)riem z0v0Z=x^$Fu=NDP8r>&7!6$ygOf(x0`YgA@{O;Ts&2|H~IU#&ox6397X95G!kSomJu zJ=Vep;xsV_Bu-k$8DbDkCp|~2Oe%Ty>rQ9kVGYcUJ_~KeRcT<#&WV>&-kMJJ*jO^z zOR2@lBwbrs12UnVH|=YM{HpZZhRwJt&A4L~$&9;5{qfLYNl5=B6*44?2|D^AHjpJ? z);)2HB>C#3Ao)yEZ{-Om9|nXoE|O!-sTo(pzYK9!DkpBnn{&nH$ zh?kfo*jPzO(iqXtcxN0l{Y522z4(7UzyV3y0(Pqmwn>^;S^m%E+9;eHnNLehUk2|H zF?S;8iAhM)jh;R>^s$3e+xs|+;OnDeVv8i#fi&IdkQS*JXWGuz0WgXAV`4CohH1Jq zW?~qNL<&EdDT{?PIzxC11D4%EM#svCV-CJ_Y+WQu) zsv26oa^-4b+uvH(dySLQ}mM9z$Z{{^^}P1D#xk`8urJ-0gP=>N>;+r5p0Q zAwLx1BOPN5bJJFQT^3Q;ITaQ$YaixP(T%zYZ91$uH00+H?XYQ|){8RjTG)Y2*g}7k zWfP$bZQ92i_BQ>#I{un?k96~ocNHE=mgyd1wO&k|urJGJ+8EK#c;m#pK24y{_?vA$ z@7Fc+*(B9gp74CO4TesbAV}L_OqUCi`p83LZ7_ET0T-Va6Ox~Z8Ag78G+j|1>j0*z z0+s%vzq4B}ymIY}tA@60X;nLd_%ku+uZuwk!gL|}d>uh)XOzz;>m63k)g<&ks$RcJ zpC7gGj+h(5Y^c}BAG@UZaePEsdt{b>9M`X)7y)72WbbG;Fed5O%F2KVogruF-q6<( z`Fi8B*w=+}YB^Do?$EAg=Oe%PqQYPE6`7U!r%P`{Kimh6XN7o0?dE78wuyf#CarE2 zGd3>-65B(ao=ZmYpEs2;Vav?+)`8BJC0}k^a=WhZ=tVDoBuNWAGhLWgLtjfLEpN|a z;=b}At*S5^<;!ABMVi^BOPftQqmtj`yxF}=K*mIZ#T`gPHr+=-?sv#6Tz~D6k%nx# zG_p+rHPsMjhkDS3nq+uQjLk@9ua%K$WTIzO@+FjuMn~tC)*U^AG{Q`R0v%T7p7>mx z&0KegYoyz)cMyP{@K1lZicyoX-{DYbQ>4AE`m)%j2GwEHX4%v{KUqz%pZk!LwVJT> z)5jq(z9X5jp-ypOYzh?4^0A-2hqXqQ`RJRc_45AE8OihL)xx&*V6r{s*JMTl$ty)X zHC;~SkVf@%2$XP4a~i+-e${+jiIvm9ZI-9Gd+#iq7p~PkSBck)@s#Nj_M%HiSY|{Y zmi!i_tb1tRB>o@qLfyc#$bPwZP_os}OP&w*tG-^0=k{x}NvR6D6{9{Em_@N|{02ZS z(ajnGGnbI$6#bYkolB}JP)RPebK0SwT0yXsAUU9bXu8o6nL9CoGksvNbr4Y00L=FC zY)Jz}mj?CGQw_tF0_7V(G8>=Q3T2L$^CST^T^f`+qmr*r+=mf&sG11YsVS`+^#@bv z$#mh4bVA<*B;$gqL@Orrv$P{&?J7NPtgB+bW3GeB1aJ1}^k}*ey;{e<;y{${ccANx zR$aw|l7S+>9GFf==tA%c{SICXewJFqgy}pextJ_=-{t(CRAfY(p!8p~)x%@E=|cG7 z^NyD%!$xEnrI>)9Q-HTmpt_`G=5h9*hhE@J=eO{M{({?=a-?hvsJh05af@0=Iky7VDt0s z1!u2QkLu`@IBQ>PnZ#KuPuN+T@fL@Da_pGTw*M3GeTv2&1)%25M_pKA#$dY+k zXBxv)wOd)}3mWSjM4Kk@vtm2zJR&=odB}4m86?vsyMdIV(3X<-fcmJLtik3-1wvLM z*|5MgT^dtW1u8j2^y}p!pg8!L2SpmF>C&KdXjB5aQ=l{m=Ut@Hsq~}6Ce6It$LPF} zq~Z+*AI%gl#(LC;v@ zzJ1bXQmTS(!ZQ(2#^f79CU8i`k&aB43!disE<2ta;nb!if%uGxV?ACzDHOa%&X5Gu zbkeiWW>V>~o|GeY-xdTYf2Kc}&GEr>;nAu>pV_IZKqddscxP94PiL;OeO2W$EM8i< zbm@XsmtFyOmOsdPmFlp>Zd4*+J z7mo}NU)&q|8p*FCsUu{){6DN6$9%;{kjz(GRKW^~c!XrVTl=heLVMbUHjQX|@AA9g zSpp?{cFE4%E6F@ut$X+k)aD_5XW?VB5&ewUCdPfgztP6wDLN6E49gmaxvtXx32iCa z7=nl3zZ5331!=RzbQ7N~c=x=JF!V?I!w7zTJ;*48&e0_VU6JC{c#5IDKK_n6LK;hLhv)RsZSX^dN$B(=sMNr?hh&hVZDub;wUbCPRmKk8}s0i-(o(u(1F_PWB>bUYVp{D=T~x zS_IR+Rw#v={qvz6yALXbNB$}~>(r{h&x*%N7mEq@(a(6(h4z*z2mRofq|V9{P8AFa z2R0{X_xgdgxp7p^?%towjkUUGk$5a;%{evwSaZWZq&Eqkl_%`5&5bt{Mc!*6nIi<% zbh&VD=wo+P6{z$e!ku9VH!Fk({PJRRqe63I_?dWa@V&p~!Ks)V=9NiYvGPRcMmk-F z5zHxCM|`$CC)B*SO1h(ak_ZyH<>FHnLHmawCe?M}ABRjdg1pnlXZ|4x zzegZ>svv@<%LVhT;+ph)BknOVDyMDf=-qJ_cZYMXs#!~v&qMNo{is5^zNh0k$3I!Y z3J5vb`;mEOl76kM2u^4}x=8oV^_*mA96{m$zo$R&bd{k#eZJWBMkky8CqV}LNnm|k zjInGPt$wt+liBrA?l~FOC!wDVd&Y|ywhPN2)vwVos54IEhxeVVJlZ{BU3h<}|6TdF zBLW|HeAD-*uZdEYE-QNDX*X7zlhvP0pJm6%bYo#;)o1Cl@W;i+A2U*D~3=r3a7 z{yyo;-|R7nHJf%R_ W#=`wwL`vfZM1cgNLP|rGK=tx{^WON@>y-mRxIl~;&+NSSzBj+# z90xXe-n8fS?*n@U&zsohdG~)U2tR-%-+pv|kha^u|FVs;&wEerN(NZ_-QXPv=sNf( z@CcaS{b168YO{1^`-?&9chE1Fws_u^O0#tLiM7#AGLD`<{$~(d?o^_x-Bist=BG*gd~L;gZ1O8kpS`+PEUq2qTN?S=We z-)eXLJ#lMxexV-uad)%^G$JxOl1S$nb!H^%8Es0&>{L~7b}IH^D4%t|zZi82jn7YG z4|`^-+bh!DLOn0H%!!gQ>>5k-cFQ-xR1h9LQZ1jt7c;227wM3x=OGh8$ry=PJ|zzp z`P4B2v)t0#DjhIwdB6-&X%+DJmq)~7yen@r7~@n(qywg&2kf?rFovfs(c{emY#rv= zl3Nq$aH;3vuB;??SdvVPSEj>C@NJcuVETb2B+>y>&jVgF#RMJ?CIy()AGWwwI$+xV z7jSjrY?5LerLaMK8y6U)#&NKkTl+zzn}GSEkC76`*jB4tITCM5gV(Z&07? z4I86YJ)W!LB!k0GsL)4F5{ zc_b)t@8V*u$og?V-Qh1V1mIWynraof&a+xg?p5Mi9-V}dXV|hNm1+H8@7>5AVv2%4 z#%M(L-u_GNxSkDm2>;z!{L}_JjYACf3)r3w^@3$|(n^qTE7%jjrf+=lB$a7>?O-*v z*4I9~(`>A_SVR}A7WUrBX6ch%y;xawKCksCY2g};rbc8|uOQ;~(!(W3Fw_AlsRUZVg2 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_cpu.h.sisc b/lab/lab6.si4project/cache/parse/kern_cpu.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..728a771fe50d4e0e7f510c14204cfb3e3ba6229c GIT binary patch literal 6385 zcmeI0O=w(I6vtoFshxhL)ATd7evDeNt!=E_vtkj#{n zDikS-pdjL+pbIw@g)WL9C~jO-aN(jLRTnOb3n{qiN&_yQ|KEM@%zN`@%rum`5D(m( z`_8@he4lggWY=AuH{yAzZ5m#W=QTEZ-nB1!-1t$Laq*XHE1_My@>wr3xpJw< zeA=o~%fa0tUL=eh%OyJ-KND_s=xZtSl{SCB#Pj+z@6r4dF@09!qO{nn@xGW|(e44^ zSHr_=wf`L{^MmsIH{k(|vzq^*Jrm+HrS*sO{fhX#q%-Jcx;nf%qG7DIwm@88-|ua% zVOQ@nM}~8UhxX_8A05pdDu@C{33cFb$elToR|QKQ|N8#=CLQkU%a!p{jbbVLlqQeW z&(umYJtv=@&$!u^7lP>_I{lHmB<)stuj7Baql-_!}1E+>UXhen4F9vIUyI%PCqXE;7jvevdH z`2LFK9KWxC_3+5P<0A(Tp!PBineTWThPTb<=iNc=L;Ic+Yt&}ZN~A9ofz?Kn$B@zy z+SERquT(3!Ql&BVx|*1WQ8Y2UP70$kVKtf+wMbt7nL-*f4Sb(d0pOCQ`)iHEm5C~; zfw}i+YzT2y7~V$PqxnBUTWXBg8Z$7(;CnSt8f-B%+8!rD3n3uTpp9uoT=4+8cAtiB zaEF<5&}d0bqckAj5*{d2t99{xUguHnevLID-Yg7Xqb2!jsR_Ox`5DhHJC(1M$MePF z^vn7p*bi#Z*uc9r;B2%c=T?oQW(nt2wbHZ{&P~qL^W&2 zZ+5njju_$)*UwmT{q!#JR;uA={O~Yzfy3g9*33IKi+JX>`MG|wJQK!`R`rMm07edc zna8U{pWfhfAssK3HojA}T)9%NQ_xhvRt^ckcpJXVlYB3nE2QIYY3oBSevUP_C9L#v zbO3%_1J=xqwV6NrMv`?ThyeBmtQEfLdcH>DQF7Zg05FEYow;$>)WO{OWc)fMUanLv z!cewLpK{8SL{0LW}@t;G~eKwh@AX0U%^asZuQS5^~)c3|u}onYl2q*hMHW zmVL5iubzQ7HkZ;!7<{derW%=}4NpF}#h*j^bP&l4%8q?NwWkFG+wHyk|+EX~!h6T40^Cx0(d(KN_Cd!iT_ceEwwMS+~xg*1(&zK2eyvtJhpx;TL;%fwvru;J8A1N@kh7#MW9ayMw{Q(lx=rDPMxlni%Dazs=9B4 zfo&Qq2mIU{8B3mW!~7e@#&0Ln-e?OpmRQHL_^6`zUD(PN$rC*qSIOe@sa_mI7Slc` zq;AGWz6~=;XMk?4?2`82Xuu-O9SInY&lhYo9f1;q8SgXj0~#1;v~2J^5ykVyYRRAO ziRTW_v~m$$@h2>Hgm{;*v4xh6vF(31$u{yBd{VspI2;eqH_FCCJA=|2(;B7cDL`8@ zM>5#N!vV}Wm~ht!wUjvD!0oh|5uG=~%!gpWe5kj?dGOvH{Xid4R?Q^ zMmJ+E3$)c`wtaVNH_28Lf9k>uFxr9*wpIfJZbLAJkV;0&#(Kis+sU4gXf>xxvqVK-p*^Xnk1`Z)P(&b1S4XAQr^ i_gvPVx!;35#>4)ZO#C?qSWfU8$o+f~=4uD%&wm4uNrsyM literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_e1000.c.sisc b/lab/lab6.si4project/cache/parse/kern_e1000.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..7570270dc5a11dd1ffa52d2a9b1454497c834488 GIT binary patch literal 11273 zcmeI1U2Igx6~`~&5aSqdzyVT7x^C8`vmDy>jeAs$Le)E6F7iMLXZg({sI+SSyEU4~NWo^l&C_f)DGJd_!f zmW+DnMH3KGr^U1Qj$}L7O=ci473%aUCotBC5xF60^=7? zP%@N1C_P#AX+z5qfKJmxyP6(zoS~ENj1;aLE)Eq2Q1h&YDlP(x+>p98qYa|UTSrH(0PGs_~C$_Q}^bsW33+)sl*?39Gm1~|@jN)iImmkii z`wPRxE(vK6&KDvCx=V;~<0XZMyP~7#Zo}P-bBGP@$ME3>1p{#AnymI~m(W(ZQ@o91mhUc+J? zI1PYdA>zoP=Y>hQ@xeqDGr~QKJa8(8^4WahNIqO7tj6LIC+lmX<^{a4?19s<{+HBfG!pKyp*1O@a2_CSQ!C&XCLnERfKiP zCPZ{ltap!YQ;l+iZWGGcIn|m~qT3$p*|noZ9V;&1L@(zBSYEzFzp^LI6Yx3 zuI>A{u3BD>aq9F7i5Gi@SM9}z5cAXML#52?KabL=SprTYWTmdpmyb2jB#o#A$fHiw zN+U~64GUA~R@3O5#_wg4;AtUx?btC&pS2&2o3JZVx>Sm-K!4ql!-d@E)F8(>SIAE& zXBCQ!r=X&%DwIl$i~ylC3I+Yr(wj(Fk?t2o1G#LxPkIu&f0VqGEz`&dkh84!@KCC- zrFBzbb60nITgRTQ={-B1>)2FSGjnEGo%}sJzWhW(-!Gy)Lj z7Y2&NDI3Ezg|ZK2<56$ClotSP?<4~t5vSZHFceh?Sm7u|&V<5Cc|yl02u&E;pC3Rb zsy-tm=AkV@WEw9S`#5G$DkWqVb4Bj=niFgg;ye_ak!if7Or*5mZx>oDi2(kbVy4g^ z-ovTWY&lzvG~ODp1`mwq=hf)X;!xKRg%f9Q1V~<5=uItOT^8T+_;4{(92tJF!%M5< zQa~sV-Y4sR_}|}5&MWLsx%AvCxuj=Gyfc4w>8V?+tuVgUDycS3!@~8jy|`I=GTut5 zg`-jWtgWz*3z7Jij*O{VO4$@Y@6RjmFSxw7YgnN7meikPXE7T5Upl)-^05uaR(Mp+ zGOxC?SQkNP>u}*%K3#bY>$-gnvn7I30p3(1uM+)#zlJ@jHQ?B?oU>d4s$C7*7RA2#S2XmPZyf!UpQP_%h45W< z)JZqjf!%6zJ)kxNwr4w|$MWX_9*tM~dXPBExpH#sQoQ-jK!t@a7h<6CQr=PC_S@wB zz+7{84t;U3>%lI`V1c1`3z2EO4g^t?mtMO{fOgP_HIM~O1 zo9f_Gi(cc^7K%#|unaQ_J^Dq0-DY;H&GOP~JpW#BL!WYD8N___l}N+GIrHQD_M^IF zuR%WXL>%D|Hyo31JYW9kZ<53)2r4 z_t!tI?*oY(=@f79FHt=DAE7Zad71dADL!zMdKeEJ5VAP_9>Mnko6NUP`8KwC>IIKn z_I(?BNZ%P;t}W2|EXANTCmKHdV|plI69bNsB1Rc(?b^;_T?C=6(I*BN{@ymp-BGu<^xd=L)TQ6UWu; z^m7tDAYf}zZB8^Iy5{o=XchZq*I|@LYfrUelM~qdig2Z)pAu!ijF-dB>zt}T; zs?$HJz1(zo__~ma2(=(EUa}IR&|tzp7(>Y{ORd$#+HH-!Nu;zj``yKwKYnqGc~~-? zl5#6sv)Adcr7|jZdHHH*Sv^BTz|MO9knSBB$UdNlW`}gxen1Vsr8a0gFt${S^5jc| z@U-E$W=?{2Rru9-lg*zlzm|?#eYdJ_G8svJ@;NELT7NZOa^6&)lk$?!Q<4=-1BgB1 z60rCr#oIq|qG8D&z7ocsjp5b6AJfUdx7VUE96`W|h=jk?dEljp1iIIh-@J;B2g$g0 z&QZqgbnShPLa3JjME&gW6 HiGKbA6qZZ& literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_e1000.h.sisc b/lab/lab6.si4project/cache/parse/kern_e1000.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..87ec47391e4e7e7b53d05c92a370f7410a97e6f6 GIT binary patch literal 11018 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 V@f6}x+i7;G$!BKd#O7!Ge*wcx%TNFS literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/cache/parse/kern_entrypgdir.c.sisc b/lab/lab6.si4project/cache/parse/kern_entrypgdir.c.sisc similarity index 84% rename from lab/Untitled Project.si4project/cache/parse/kern_entrypgdir.c.sisc rename to lab/lab6.si4project/cache/parse/kern_entrypgdir.c.sisc index 1d79897ca995992c841d05273caa8f53258d4989..81c0f25df45be7de24d20b4217169196b57e5a0d 100644 GIT binary patch delta 94 zcmdm{y-jw6$nCh4~ s8iKR=8?1luY<|enD=;}*RGLEpsPM<@c#g?^qB1-WfSim+@f;wN00wg$J^%m! delta 94 zcmdm{y-jaIRKQFSgphjG6?`JZW|&1 diff --git a/lab/lab6.si4project/cache/parse/kern_env.c.sisc b/lab/lab6.si4project/cache/parse/kern_env.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9af0b784093b2c7f85377b7ae00c660e3196da13 GIT binary patch literal 20468 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 DsMeva literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_kclock.c.sisc b/lab/lab6.si4project/cache/parse/kern_kclock.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..ab0a1690c5d5c5cf949af6ee07cb462f996a9b62 GIT binary patch literal 2820 zcmeHJ&r4KM6u!-j@e0Q&w48n{W)VS3IfBS6BK!w}iXyFrIzN~h)KMmM&42n=I>6Z<0r#MwC*Y^x1bDxb5N2hx zoO;_IPjdZWk1^+glaaA<%KJW@qE6&H8u{|0lNjMvHXO0k;UvP?Ji?OBM$b6cP!zFx z2kyh+2}s4j(#A3enmXFLXoyfWw(oa`7T%$|S}RO9ilxk1ECv>G!?DUa=HtEbiCD7I z<=}d|9M07W-|r2#%+VX>m@3t415<_R*}@d^T!W2ve0r96Z^mX0&Ot;SDR(dLY0bUw zb2OY<5GOfP7#teT4d%w{rOD!TXT!xU9l~ElKj?H6no#ax-qV_T-{*ar(YwOvx9ZjT z5?!NU`EV6rITL{86>M?j+5mtmi-{!Dt}f<17Rz%CO0Ym|0t@#f`ibbTeLJv>xkFbV znNy9Gq*?1BV~G8bHiN_q^~~)xYB#FoTB&#e8x_tOvjIoJOvW{J8Tdn3`gu1WuBq^S zl@b&cub0X?8+6*3bu`j*jbuSZ@Ez#po)=QgO%za)C|fmBe)MG?WpCF=`m2emOOiIN z(Zu82i+0pRUV;Hs@CJ%Y_XWt|yKx&gor7-mV#;pymj9%B>>Zal){Gw8A}93I`f!E?M#tMU8^JgQrgNEvo; zS9si7a2Bst`gDLt6}*CiCtfxS7OwtQ!5S|mNh9A?!6y))$FT}wP&Pt*7;CbioW=kB zk@w@9UNe|mk&bTXv#pJ}c`uUgM}%gOJNK<%e{;x(NQ(NbV`$#jz(-Gf^hb(5T-GG| E4b&zm@&Et; literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_kclock.h.sisc b/lab/lab6.si4project/cache/parse/kern_kclock.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..194e613fe3057171ebd3715c83964d336e9fe795 GIT binary patch literal 4158 zcmeH~O=w(I6vv-QT1T5?QY{)wg-2V2`b7h!6a+~c2Qnt@)MS)c!D*6tnG7bA@+Q%p zh`6Y(x)5>EMGz_}=tdC5wTQT}o4RrlapT6d0fp!Pci%np)oG{HkA-;1dFQ-y@BN>T zd+vRc<9mak7z6{y#6e#W9Lxs6jnDfW{~!rp{qe>iwyR%X8$$L6|I>e^0_^=G;sZ+P zeeo6XelhO@Vrc7hC7a3a4x-o9FQ42O1UnOz?D$u!>Xs%Yo_2r!ZSWqMd9_ismZJx} zWE?-%OSZOkji2hoH+zEMkbE!dtt|a_1^0)a@T~MJ;?D#+Ctj1Y5%G?|-xfb6{!Z_& zYCe9=r;{U1TSjAY(MGNDBnc6x#M=GO7v@USx!JR&>B*VGg#qoL^9xkp_>!JP@Lb(iIi{e_e8m>eOl@*bYHewCvmAxhjl*+71CbmdCw*k--Ewym{Ub@5l>ujOzEGMiPQD_Q zHo4zVbJD}2cY|dMjFO~T4q%7ROqR~RG&^yoG*_IMEuNQ&y6yAxo%HbN-Qa;SN|GT{ zUG_sw`26|Y%Nipq3&BZ`j@}#H9SP?FuY+mirzhreGX=o!7264M(u1RS1Gl!(unx~H z!Kd=`9>7kMlO7zsH@H}MnHv6C|e(DD7SM|aYrqyH83GOLXL?+L#H(u=LUgBFD+);JKefC-O=F&`Wta|BZ{Bsien<>GnhJ-8tsAPJ;>1n&qeBg;Ee&tw zxUSFU_EY_?6F!x_U$~biE7_^H28M9)rM8tOBPqh+Q!3H+DdioqJw(Tkz4!W*ax+0M zit*Xn(AXifok)KPq((j4KZhKsl<=L|tktWPT2wx#7Xkt#^zw+LIm0Gytp4!5#fGjS zIk8mIr26Bi!rHb%K3BJi?mZ0ez+KNi{>KyAvO?AoM zX~NeupM;?o#Q1H!u&_gByM6aGqrm^b>+97<=K#P7M22E>giY^_ZrRl?%haH5ox^>B z1F)X(^0=g7VhdX_-|fWgxG%~J8=GsaZrz?8ZAIqk_I^Ak+vV-FAGxpTGu9E!Ki!Y5 z6|}p|YirB74lr*w|x MW1ZjarvtV>0hgkla{vGU literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_kdebug.c.sisc b/lab/lab6.si4project/cache/parse/kern_kdebug.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..2ef2a070a4058b3e66fcb958c062b1a0fc7b6a3c GIT binary patch literal 10643 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 literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/cache/parse/kern_lapic.c.sisc b/lab/lab6.si4project/cache/parse/kern_lapic.c.sisc similarity index 50% rename from lab/Untitled Project.si4project/cache/parse/kern_lapic.c.sisc rename to lab/lab6.si4project/cache/parse/kern_lapic.c.sisc index 03cd588f2f6dc656bcc27d32b7ec7e79f2955ba8..16dfd8c6552b38c3d3b45d9804ddf5b50da40c18 100644 GIT binary patch delta 1164 zcmZ8hPe>GD6d%W#)R<+~)mJ|0$R8)PlenAzmo;wBMUkjY+sG9NS6$*&WQegkL^2`*ys^nj}Qco+56H;Ow&VJ*R zw7mw$@0yVIZo|3nx+H0L;e2&HmCRdk9yHBJ`I~V5G>!ODV9E^EI+&@ZMiAn#9L^Hi zhzeKGkn9BZR@g9%iEHS)r-f$w6k2V5CGSCiKpzyH=G{JdY1Mvu>2@1oWZLo;vrhs& z$5~KEzZz6U_$;1xUbQgJPB~AQ<~*hmK%oC*H8^Yn>?wm8#4;&~Jfl$w@B5RR#|L$cz=>5FI-bG6cw5IKUi68EZ-z zJP2T0HJF*}sj+p|SVZuGRr3~t{=&vN!Q-EbEqw@QJ=F;`y4IjjNB1a+ZOgJBeHUoi zp;NNWWo!UGcAy7ooS9nj8hJSE9`J7UAVlZ%kk2_2RrCGDtE|J@!zeomaOOv}bd{$7 z>Zs5o7RDo{D)dX=mA8>zpt!12x_v}my2DS`8+1C|@mb3AIJ!GWjd9RuLq!@BsS0y~ zvn6LyBhM{s<107>vouc9r_Kc<1wDD|(x)wOfWa+gFn5br6?nJns*ytAn8nNi%$(zd z=om9HGlSSKft{L0H;#@Ofy-dAuJ#EBu|7^1lN@%pct+Y`6|-V@>xFFIvo9y#?J>fD zY6m*ig|mb;@chxT4qm*MI`yz8UWdusIulek&%UL1eZI}{kN7})E9~_EB6hw-3jY)q X*9@z-cJ^gh7b<`MC8E;#dZ<`w@qm;d9dR7aTUj4=W8xX7TNr+=}IgW4R!8hL?NcCD#mh<7?`X_aX*f% z?L*vu7<)aW6GFsQ{AaX|548sPjoN2&Uoq}y{B=O*qmhKXyej^lMk40IS3}))ybl6A z0!hL9+%>P?gEIwBJFPwRfp=bt4)c*{+8je@)(JZ_0WLw;GrkKZ5QXUiFPf7u+U~J| zXK_iadFZm&y*oLzL#IPTPGBA6o!Y3mK@1L9urj1!fmIZw13+SQfiwKz{)`!fVY;`H zxrBUIUCc)SY^Veiv%*95L*}b3L!EXUSTOnnS%PlNSEmT}qtG>1I?(tPUw}8od%34! zTrRw$9*pnPdA=cRCIMe`T#AJ96w{PlMj8&$gYs_oAj41VnM4WTLYJM87q=><{oPP! zF8Ow`!oH-ffZ;hg=f({AFumHuVM% iw8bUQJ7A&R10NOzR|}zMPdkYW$-|~}2TeJ1= z2M!A@F;=No!@>m%V~B=?b|V`T7A{D1;id@-+f1Sh5;y3Ijp-yBettjqchB$LdGmO0 zz`l*~B!_c<=id80_uO;txxe=t23AGUNEEHuCXTwIDA^iCAN;P%@rRP|<-dN=jqdUv z-(81X9{r#GS9XBdKP=uThkhacrT9TH&nv{xrI%0i46mE-rv2H~QS^?uSGvi;6FumR z5>L1O@=^CXDS5O$rIsUbp=Dz1ST5Pza1P^)eyxFb#II{n&uQ}ezGFi2S9#2m5B*$=c#EGb>VOlq8$7HdZz@SgDrECuD{Vpj&=9a?+cj z-OLO+qa;!IJ%RJ^0yERa>8bqL%ITVSh=pqx9XaVO(QanR4|SroL;0DRVihyTHJF7f z$s9T9&CqUdhP+A%haOzu(Cpx|vQdFIpAa9C}2fXo2%lVWhm8`FiMmD=?qp(LrB0XH|E;?4j}IsKFvdO{2^VTi>> zOoC>AJeCU`0C<_-E(ffvIW}3GI(0;Y+0AXt5 z*z<3X<*d-K9$o^_hZmFx=1LV>4}kizD~OM6fB z+&e6P2TIO-W+beX5koH8zyB$6|a!vVqv4?R8l>y|$==+peXUEArJ7S(E^H zM2vh)Y@lA3fzGL3sXL=Zp%>_x{p!Y6EdGL*@-_qYx(ujK6&8a=NrIm#u@7CfiEl^{ zl*a=}z?fz_^=FV$X91+0By|HoE-UdIrqXq#&6TIpR3fAW@eS$byZ867obY~OC7g7L zrm0k4e!4;nX1kLPo~2H#wuKo9#bVmp%ts|MYL_~VzMhg_xzDG*QeE&hmTq)?-B-6u z(s$BVQku2JV#_`x6pLxM_O&6U$0zgEMrSXmgrD~{;-~;|c3931Zm2s84BSa)Ul+bP zYo`JU#bVm6o#oXhC4#eNL~o4~Rn>3wVJaV&cUQKj;k~0=q?q)oQcey|WmSvkEYX(Z1mkugllY7 zCAVud)e*^cgK5*nL7|xcdHaHvRY?G-j>yNw28u2YKrjBJJ!n=X0ee=A926TUx;OwG z{88H~c}P=8;U~q^38Xo1x-SKg>dpd6J4x!7Ac0A5MnQ%ki>t}HKHn!GD?z0)LL=Yt(98<1ab&DAU0HVji{E>>e3c8 zq89SCJVkkp7&Oi)(tbCXt`T+NTI)7wEiaY}tZ1#+O4tB3$roi#4|VFMbp1VPo#JH>O6e$D&W>chgt*V|MrUz72bePWL~>8o8p_+>8xEqIA#S037M|7v#c&9f>XbMSp8bD zZN`AS`;+&WV*fqyuy$eYt?BFn;+33BsNDrrKaMHB9i*jRKr*r5vaw(0Ch51fyx+*P z(NZs)7c^mm7`aJI+L|s7>h0*gTs8pky8-zbv4NtCL(m`I!({^)g$rrF#!MH2&I=TS zMonJH=?;T_Tmc^PZyh7*D~K;i*XLiL`P5om6%OV9Lj{q}0~ z^(wyl9}8B$Zb*00*J^wPg?XLy)#?|1S+3Y`P1CjZ)sAe7`W2}#=X|j~DnkVJ_A7GM z*Dss}28TQ8tko|Zvm8c3rfco&-Rc*WH%#U0d{)?&zCiz8IeSK~^~&S9!E#o=NGYzx zcJ-@nGd;7GTKT|E z(IGMKkH}szrcD=zrAMDv)Robq7xJ;w6MPK7XXbD5a;v0aqKgB}*>@T-t!Sh;`WQgg zlW1QRb3Gv0W*|)`{eq>{L3qbwc1kX3?Mnz?UlX&RNHz~p(=~#sE2CzhXZUQvoAcMj zfFpSZ)O3xY^cgMwY(YA(JUjWv)y6l%WI9!V%=bC{YOs{k5c$sRl+dsK?qq4I7(Y`| zn@JO5c86zhX`3#@9X&Oc!xN(foa=i-B2Wr>+j$z#-YN{6|8j)0wJ!D*eJU)sR@tUI(wr zvHAYAxL7$I)&}G@r>}!J%MP-;opjd9DUNLsn^ZGvw|2H(+E{P>b+9qKr{wXVzd*({ zvLL=8-E817SqXR2<72{SZAZkU(l^DdoS>O5%<$%?jJ(%Wr+JQ%s*>*GNaRJ#lQ!*d2@!?X_p8CJxX}c}^ za}8^{pmp8~!a*3ow{Vz^I%3+o-!-x2;hN%odQr5muL9*$(K)Rt`rD}6A3o%B({dWy z-MAE5BkU}7pRQ;u)d%{w*s4LxmOWC;wANj zjWk`H?z=SlB2;aA9dc{q<9EayAtZYXr0E(j8FgjU?A9JpFMD@Z49@q(ESsWay6~v< zoo!rZ^J@mIFzy42d%u6vvSN6NGo$l?J^pToWFG73ROPo~Xdy5ulUZ2quNZ<5dVE#h z&H1~IzJmBA?ZRkZ-DAsI^Z4UFeOqLcw1w?-sOff@orceOW)09y{nw{!H%2K49~T+) zz2Yr(-crZi&Hnw)U+TPekCNc8vd9`rVb+z-cG z*O-g{NXM*aplEX{T!pd9dd}+*R2=BdRJfnI&-?aQyhNxXIqLTkvCX&nv~IM|H>OP? X$xANu?Nm6d1HPRPbVq#qTY>IBGpeaS literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_monitor.h.sisc b/lab/lab6.si4project/cache/parse/kern_monitor.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..e857ced0b5ce7def61c3eb980cf1143db8571cf3 GIT binary patch literal 6180 zcmeI0O>7la6vxL>;336Vu<~Jyj>Ty4D_y9mf(Ac`;0G;GNOfU(eP4Z{yw^+%>BfYG z(JE?8GzJqF-H^b>urXoj!UanbgXzKrAu(ae#>he)|G$|z^KPf*wJcs2#uLsvch0@{ zfA2Z>+&?qzU*~zlp4Yic>UDTt-!{*?_G3q+zsjQLf4kO&@BGgfH(}R!|I2?R1DyR1 zX^#T>M*6+<-b6cPk?XHj^H-i)vx)W%9sVA5%*i)DP|xEt2@{#S@n_dPa`I?>Dy>L1 zCYFBIv0AdZ?wn+3WS-Z+v(loTei2wL0sLFf8`VB4z#pWedIlRsBHP{c<>XVzFA#Ul zFx-;%v9~R64IUYNd*9)MqwfwLJTN?Xcy#}_a1FkGLu+1dJ&je2dm5gb*UVz8*N zFG|6oO&_?-K}J3r%aH|>Fl;Wvn4EA&$IBCw@9BX}Ss7tpmI6#W3p{^zEN6?TTNT!Y zi3EQaz+|pguQcA5gQM2bM1p-q3OMa7_-*YPpGg=-6(2nUz6tQLLh;0OP$-rw@-(y6 z%&Fzpa${)Ng$(Wrk$p4r0`Ww~!ne%@{EVTWLxMl!j9_%EufSq_oEVZ;*$DWsCCoTwb zNJ?1l1;me-?>WzdY*SgtJ{DEHcIW9vtu&~iYzBZw68osEEridHOGGWt8~8YTtC<}O z0d-7QnCj-xR}N>Nj&sPhDM`;Lhc~+i^Gt>)3@F7e+gThh1leGZ>kwy>+id(b zsbIG|!2%Nj$hPZXjlbL)rfw1QwGMWr{PmS09(XpxU-I3YZSohq1m0@%7jJHeS+}j| zd!o+d$K}&7TwK>jCs;`1HQkuUF>zi#P($CE=J7Fef5i+}BP1;*Fh8tkvc( zJ`NGHZd=j!M4idcO8E<{ty1iRQp(nRzQYQ)#R}?5r6qp>uv3bCLTaG+d`<_X z`C6Ab7cmxEyl9gOm%lD4u9@Ur)A+0ZP5GQynaey*qg`BW{6$>Ug;_PzcI#L(wXzg{ zbt|xugshFfZYcpbonV0>;C2RkO!y>}AgzY6i1}Ivdnf*SOb9)R5`_t?neqn?)DkxI zYBhgZ93GGDNZhH7-FN-l5v|8w5ZhDq4XVG%&fO?N)ae+mlRIXwuHe<*<1u#CEFRua JO^6A>_b-!b%^&~( literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_mpconfig.c.sisc b/lab/lab6.si4project/cache/parse/kern_mpconfig.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..de2091f891b5bb4dfb5ffe534c35f7f763f13fa3 GIT binary patch literal 15692 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 b=(}Dx8^=QXmyzzp&^{6AwuClMl}7&t>;r5& literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_pci.c.sisc b/lab/lab6.si4project/cache/parse/kern_pci.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..e65915208a99321c9fa54f6b506571f427765ff7 GIT binary patch literal 19571 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_pci.h.sisc b/lab/lab6.si4project/cache/parse/kern_pci.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..d64f67d9fb27f388728ba8df532278cf997ed9c4 GIT binary patch literal 5991 zcmeI0&u>&!6vrQ>*z#jKRY5>drpEYVVMQf|5MnfH0vfSEQIy8%{CJ(d*uK`877;?M zi4`{{U1-u6W7rUJV~p_!F4QhGamfZljDLV^j2ioC{xdh5U;oeCD?c>(HI~@zV@)rE`O>mk6-- zo5kzp(EH+##2GR5ZZVX5tCBt3cV!*rucf@aN`+IE?9cD{S#(B0ObfqW4*OE2?a!_A zS#7P5`eNBD$vn1~HmbZyY-3bxqEXFzRh{#(_L2Y>#2-rcg!m`vPp)vBi{b(C4z(ST zPQ{QJM%n3a=&(WCzloCb2Z31|+1dBZ^9S?KK7C+ce(1@)`8`Dq)GT!uLRVsV%1qY) zP$fk{u%WJ@{|3)5P0W_v+-?;foOxr~t#yq*c33U6VrWl7f^NDn+N!ssAXr;hgR^$J z z_8g8ehf-~b%Q-MzGQ1bR@5$gJ)W2mu8w8Awh*0$slCfF8G_1;M@g_0ATf`WQVWvw0 zZ(8cfSba1EW>iD~_W-Q@fx#GdN7NL`TU7=d9qWtnMpwRR;G%15u;+U-BWh#~?xdFp zd7l_y$_7jwTKujj(;VeS;=$moMM8lt6erxGbjclHI7yj4n$7s>qdDf6-LYJA0yv;q za6c!81|ku9kyDx3(zC=}LJ-DWLHmG4}zLNd@v%9hVm?~xo*9b4ENBp)PShNSLh)stlQp$h`DB(iCxHZhoj z4p$22VU91DR!?K9sZK4JW%uZQwK5a_f`J4BOw*+?X)_9fMufoW<`f^KAjUW|(oUP{ z(x5nI6a+sNb>Lx31`odR-EK!Vs5BO7w<20u;oYk-JmD} zmfRju^{HJW&+Q$4ba3axJ4Yr8#e8jQwpwy)lt*55tNw@rI5>X&Mvgs~ypmX*)7S37 zM|&qCfzZd&T)#d(i}ATxDRa`DeY-ou{^a9BnxQ{)rk@a}6CWQ^g%R^uUC$Wj zb}@ME=#^OcvLt=z&!I=&B~I$nNuc>7gM|?>cV@a!b^V!qvFep8E|&tV!V>x+ZxMrT zx-`0)>eO;6&?n5FqM3Nk2^!h|x?*?g#5G?Eup)_bUioEVcPkG4($u7DlV4_A;?L+o)ZBp}iB?KDPYwxc-h<+!0-Fhc&P){#=*a#dD?l<(7Rne(KKN@LbCXNCICF7n=j-{+jf=ebrmkVuvO0clL%9qciuL;?nUnsI$z-{+ zzvfjG=kB9ZUU{EJ<1}FR#PVKAn+RPldP=M0IOp`74GZy45YRIs99l6(IhpoG->hV( zFScH*qA|_C4Xnt5W Qltq?BWP?CFTp@J-0NL@TXXZ2K z^ErJ^-MaUD-?{g``<8lD-7q=`3PG^PvCv>-5Zv~B5PbjdN18qjOMT?o?~i5KBVYO8 zINDLc|H=QB9^lwdh8}=~-UWRh^zIQsunBWv4<)%ZxpMr5?=BqAm;d!p5EMTW1X~+d zj-SxoV`Zuqlq6eUcx5a?*lYJ5!kOH|Hy||RxVhxo(pm-2pAUe226QRj`=|E@!G}A8 zU|c!~j(KYkd;|6Z=o7aD!D8sKw+6vI&|B{dg2~Wh-Wvpeg#PZHAXxaGAUGL`9s&RS zdxPNX&=27C7f}9ol--7UEq4dOg?9zPCt#m}-4i-yOAvhQok8%=u!r0c1oPmphClt? zL2wuJadhCn+!+MF$9rw?m%+ag{^xEBf{We}1jpST1kb_l4Sg^CPLy{bhhKJp3!Qi_ z`t`gZcnkE$@V|6^5Ilfn4=-XopnvHOf*&jif(_8Op!~BvL2w_+KEEso&Vinf*GteQ zfWC_N&%}ENqwWi6yB_i8pM!mU{~-7_2K?ZIgW$wNg5bf!f?(v~LGZ|=AXqd7bq@`K zKcoDQ`vt+XsIv-n_kuRz{Y&xs)2Q>g3CJDorl9V-QSWZlpE5BBdJyMolx^N82reEI z1mDHllG+n)t_-!23VJ~(|q(z2HSw~4BCU>Tkuyw zH{taIcrSr^-@sdU9~cC)p*t{Xp9c3mn}gua>x1A;&?m18g74gbIRbqJujk;s@7x#! z%b}y7OK-w>Lyx;T2yTbI@9jbG2iR9pZ#Mkto3CSgmO1U6^CgmGFO!wjVnS z;{p9O;@y`Ig6mOsJp73{)Wz$IVV{Pkt_^~pVmq#dE<_I5o7iFKp>(8tUf;{w>YlGl7 z>x1AL*vZg4FAajfL04XezJ=|&1p5f|TW`ZYauw=dfp%A7zQBJ1b_vRlhJQ86Pu~y( z&6fwkN1+eHAG;Aul&^<<7WzHJJOzDx7=oS@^O8I&E@g0Y2uA%z@TvCp<44)huLtD%`=us=bsdSej$ z96Dnz#t{1PCd{unL2%wFL9qQ~%z4t{5b@=;(`@qo{1H7IA`&I12vk+tCsn|wn z>zfhdv>-SV`V3xw3a_ti#y)_YrojFhy83kV@msLnXMlrxx10$J?L*n&us_86EqL!^ z`v<}Ovk((GJu@3)2EQM6H1x8GSic)l4!t=U1b>BYpNIJmZ9#ub#p}O22mLoP2#y+o zGT5KNUjF_d_~QNOn+H%1y%oA>E9T#Wn0L@`!#@ahe!d*r3*7{5SrG)?X!9s+={_9K zg7A1W4VGdSa|Ged9|Jp@Z$u<|oGO(t`6Q!Y_bWE178J8hX=*OjT5KyWLGef^M#aRe z5cG+{EF4ryCCqUN5eN3IS-R?yWy=$%!#QdF#{T8&M)sb$5by9^%vBS!qR{sxLb8)m zX&>iWoPC%>y19rP&cz#hKrzitXcJI2r;p*Y_hHJygq@U1J*?=%EOpBblGi!DS3A0w!1#kY+fo zSjP9*TU5$Z%SMcg$M+0wn85j{zBd&6w~1M8I(^@!b22z7l{h@1u+JuP)3doub5~O# z>>Lg+@mM`f%!)w2J^_^_HoQ>+duI{e#1bB0`7yzgQ|=v>4d|WXh;i{0*g6yf_Kg4- z8;6SkOPNWMf3ZDm&oUR=&G*f{BgVz2jwd#DtENnIYAM>|@D!w=6tn6W`o4~F9M&(D z4x%w3^B`__Q>LRM+1Xso7m|gpJa=D?FxQtBt0*fB%j#o1BV5LC`g4T2i?@nkRs{O> z38*Y#A`_yCaD)k9l{Qr}D**l11W*r^cVK3j7Qn0k^nHNJz;Y<6mN;f0025e-7(7tS zSNu*|#OE|(8RK*3b;J8q--vPX_y^~Q3#mwAlTfRmHfBYm@1w;>szzejWP7SmMsc** z-(5nP6^gzO6;G_jXl0YRbYZcQ>P0ke)&h~tibUT>VwsatX^L}gw5CM65VIniO?S46 z#T}Qmc?71I6^p))#WmeYsl>q!W#EpR%DEZHX>ll&#~X@Sap?OvJZw9uIV~EKP4f#G z{*6ovPNTEc+^lHyeYCpM!YxC|YZHBi!N8bA&Q06n!6xyP}g)iSsuU zcHkrqmQZu%bhMrAKuhuF5m26sQOpWNzdopfCCHAZ0(CXFI1C3W0p%fyVpbUXJ`5+T z6YoF?6B#G<9;e?_D39S3v!c=W(JB*;D{bXfdHRXw_NiPhlVj&igYt5SVpbgbK2AKV z!8;EZMEO*Eu>}h*C&Q7@ZYZUje}tj$!&FuUrZf0t5T+y5obKviK1V@&pcJzL(f5Id znNPkwvyhmhp-Z3?v%=8#VYr$&@g4{@JT4!cJzR+TZ-f#@F)IvxA10m+_rOp_!5kjh zhYHDDYpPI`Nj74aCGHp~Cm+SEaP)n+1S?8PrNMDJ>_%}6IUUN$K`|>FeIKp@s6EGp zv$PoNSSSxv6tm*cug@8L@|BPA+?fb^6O?Ba6te=+_kp*!rOU(3KMD0NRvTKk`SgA9dBW_(yZLLEU)r~>@1iwD z-mecDpzBoMEMju=>HEetzD$l+%kXq`d}UniYKOf)1G-+d%ql*8U;LWAUy09)9iD^D zgkGZXm(n8qNR=@>Q=w9NyGM-UdrnHF%9#zZdso8a)hy@+RW+*(r|;YFc;x-mGVGZB z7&+h7*kp6w`sRF8m<`>iie`nN??Z4VJMrERDI?fDi&6Dt=;dm11+BV=W!2rYt!Koz zIAN?>hDnT#Cee+z=0LAf6|>rW`t>(|f3*yQ@6X`rPFyOrB%4y<4IQ2koC1BDYMK>; zzK>Bk4dLG98HEaieZ-Y*F7#?4Hqok&ST>-KR*V=IM|f7Y3IY*MgRmo!T%6#U zMvlN0;&kYZP|D5xgW!9>u*?PL?n#-xraE}l`drzC-EE39(Ci)7SXRBmGGFgF6q^jV zC0+@i&86CK`H{(`o0g*Hnb6zV1Y%Yc`aa5@Rx|wLJv$4R3JgFVS0R~PvNgq_ehc(= zH8m>?{rZ@RY8mcwb_X@YbfyXmGr9RB%!6)GJ+mUv_Yp>`E{%6Gg@opGKHHvL+=E(4 z=sQ^*VpatDJ_6^0lTzsb=h`Lp0hIyi=xQ&dc~+i04^m=%Ej>i{(7(#@?Yp4l}+-vyNZFr(Zw3=P(xz z1U|cHO6BvJTsPwI-tT*$6tiN`_c1D+!Y&$=kx%7tN3aq1=Mm?v(DyyILKE#2lOH<<_Aw4t0iP{c*AFD#lDmr~%be1`( zu}|ld1?&_AbU^P@3$p^y_W>%W7d01+rrhFeA;X#43B6wcvjWif0pg46;4?Lu&vqk3 z2KoS$Vpiel`@+X}gh6LfS7&poCEb~7<~^w_^!-qZSuyDQ7?Z7L_{V=w%AFQRj;O_s zbiSz%jn0BTqzGn3qwk|}KXOtk9qL?L1P|phUOq#P(iXHPc`qUdeN;`%3P#@t;|}G- ze=owN7Cnp?^G*3QdomCGfZCfCj=m2U&+Ra^3>SF2J73N%hD$f6R-dR@>Ds+m~<>DLDiJ-uw2W48eMA<@i=M&C!{O66o2W0y;& zZ7*1ewjWk|v%=B$;ka};sl6A(dNWzDiMa?(KcePlMWgSd#b?Ul4s3ftcu&qhF@;-< z1|L-;v%=B$;kc?f8Ae}nALm5jfn&Scn-z|}57(i(G`0Iur?tgYp)Hleiwn_$%kan9 z6k=91`aT+0ASbnxbF@NceyTHvk9Jm|-MP?DsJ&U?==*S7gPfE~mBo|iKK8j{c_(+c zmgeR(&lk^weo{@%ibTIYX_8t-&KC!el7$YYbUyS`s%};w`aV#+2ZxeUI^W#MnZdJ` zzZc1@Nc4Rqu5nI=HZyS5)85>Y&i12OH}oIW+^lHyeKamPPD-UCoojp9k+Buh4HXJI zTJ1UoCmdP!C9j7*t(IoRr0-*LQFSu3>A}5>(_;zraWyq768-w5TGK<9c10XT30eyM ztUzW3qVEH>sV9B5H%av3U9pI|G9S%K*LK=D=aU~9)e+_mgEhw_AXpjpiv zBPR7ZD8;Ne^nILoLbc|*))tt_(LBw9@mqarw8vXjTscIRH*3{VERk`pl7H}n!ZOe2n z?n8a*m(|X!i1dBL`2KvTT86~%nC-oKCISc#B(aRe93xJ{ZwX{pAo@N~o9fc|Kj3yNlR)Xbt$-Jy{^G#F{Uj}%X8inZe(CS?y0RVUSy%r%R33F`baD?`kRMc>D2vzp-_ z@54&ftQFzmSynUr zQ~QkF?Y5n`yOD0h)2v+gHb9?N2(!Y`_u=BReF%Fm*F7!~8=?OykXeD~`#_zlOH(RM zE5G8*m=-%C#_#bK!@GbNquS-rAE=>OA?f>&1=Xb~m5wUEBJ!wONIX7}Y;RAs6M6;o zhiYn8Q2IL++K~-2U4y_^LZ21Xtf2JkLu;LGRCR6#o`&sMji^^aeHC0Ps!LP*sGYY@D2my%9p$b;{hz37Mj*2S(f5IRRF|gq8C?j< zHzhkeQ@LJLzZv>lwu6`zioOrE#A=3rYM=XrsMcghiq9Ey$GQdjJGD0}7JVP9TXkt_ z?^xeY4t*ado>Q%*f}d>`TT|$}@bQIrAmSg0ftVGEzK;~2eg|1Q z{uu?0Ur88=dlcuxZO}hKDQ1PC@56MnqNMhGD1Vw-oHch2QMW^1QB$)*(f6Ua{5u&c zYWCbY7oyD;=%3ZrtWflQC>{Zv)ILG2WR*>I7Vx|=mx^~n|DyJ0g`@Ao@i5?|_EJ&7 zCEK%Y_R+%~X!%#QHY*tY`mkDSdj*!wEKKFHO}z+tC-gssGbW=cM-CX=-Oz;U$~+1OxZByP+dQGbfwfD3NswLf+$`STn z=pKTZ6^ye>tv{NAJnFdtt0oN=@`+>ibmf@ zYN|y7R+c2{@V@8s+N1zi$GbA1$8UVGeA=7XR3)iq^;-;8Ey)qL~$qzK<5q zZAd!+Pgi;V{Q>BKLYWncz7Mq(^_|qd$B8G}_~pXRW?cR)MU!pN$s(B*iN252<1nz^ z$A`;zZo{YBiiHJC=!4KhgfS}&eIF*icuX!sd6=%c_h1jv7473bl0F2TB9d8==Ytc~G;cL#xUO+wqJwhO}0@3$@;wK)pj$q*ff<-(g zz~=&ZeEujjA(mOO==)gbpuUq*X{vKQ$7iu{Ig!d0lIhM4c*{}$W6-H0niY|LePXR{ z6p=d>r~2PP`Rh6qvjWlgfzA!9+TYq!y%~=|7Mru_43~-R(4&PhD-3-f=KL6@)-quj z-ehaZrOskTABVnCD6>M*_o0e0)IsG}+^ecCo|#d8ai?|MC-C+$!k86?{!YN)$&>=K z`6P6@FlL3JzcVnH28)6C6!ZjP%nC!_hv`OrC#BL@=h_UnZzeUiwKwr+NVqHfJ@h06 zH>=?EeZfbT!*Mx%L-`d)lGQfKzDIH!Q2}o9XMS zzt6U>V_6FJ^E(1?<39^MRk7b3iOsU=*xN3wj*ShGuzT!#5N_kyN8KoV0(zPNr$+!R zs|NI5G-6x>>O1j1L9 zEkc+Tg1!$iH-_*`qcVb{%(dYQ(0L*xX+hu~z_RL&+SWf}T%6!p8yuk=EjA^a+EVR!KECp;#V_K$W--i)LEpz1t(Y|4QK&H3M_hxx1WgIi zLaRPvS#=-1h*ctv7>5R-Fpcp0*e(r5|1z{q(K{5K5m@G;?}^uxwpBxjZmToDlE+E< z6=+V;&8mCo`?@E-Jnu)S3Af$8TI+TM{FVXE3VFuxRcKxavqI4KA!b^@@Q?R9M-GwA zHZuO#pasP@tN8SN@lPId{H8`OMqh_6R(!LHPrrVARF<%dY%%Ic;u{aGyy(c;m!_V= z8)pk(Rsi}wK%pesYfM2WQ5%?R>a}?gJ!qa~};d7Y|&Y*vUo~!U?6`sB?d=u(B z@$O*SdWD1u&}===)M=4a3=A?>8+Y@rzuDx>5Bz(4~Ty6@zuU7*R$#vjo+5K$i<)RtWk&L_CcH%Rr9BfjsLH zf)h6UHM4HK^F8QFQF>`X;fS-06c*|2;6;ph3tl^^)mi*8Gmbj1S^djBK6aP#M!_y zl31j-gE2VIiO+^9csCTLW%tA*X<@Z*!Tp3b{*K;iy#FuIixteQvZC+HDn2U`XdMb! z_T0`Otn#|>3@WY@a6PS(V_9`_y%&ra7oQ!T^Q;Ik+1#A#M#z7KULwY&5eCbu8DntD z9#5(^Baf#gxI6s-dYKqz)p7KF9Ty*@+Pf3;unyx2^h4-Ifi9<2hq0_WnayjfJ8bY3 z2>b1v*+rsAc@}zwC|5=(EUTt$pYrn9c$d|qu+w<=|nGGg&k&JW>JT^EO@jg=ii_=i1T(@^%l#jQ`wGG z;=MIEhj|nW852)$nc%ql+p>i9b_rQ-2XS;Ac;{GAH@f@EiEk^Qpv zzvGPu1TZTA{c1q}tZG1w1ds^tzx@*WpyHcVeEN((8XA>&0-gzlxEN=A61^bR+`u9D z74-cgm=%G(k5IYOIrtD1vkP;)H@pb3e+_*|@y#ke{rd4M`EUsA_8T)uwp#D}24xQ` zwpqodU)}p#X1qK$zU_nb4q{_TdTS=f{{JoX5fRLaK);&MF!SZHl@Vp{)FPxiGdcFp z@1T#0U{(bBzW%}6Whs>g>7NBzyP|#xH9w%(W)+*hFLveZhFf3hE%v?L(`Mh}$0ZBt zW^Ve+&}|BDR^jQ_4?oDJcjVJOi2Hl!2Nm0_V$=7jw{Q$HMg_|D!;tw{xi5FRT zEI7X~ndMExKR`buh*?4C*N1p+8nS=DSj67>59o&#+^mAr_XUr4{vf^6-fX{U{zt_A zh+>;nZ2G>~mGcSq4k}Bc>ze)70RC7c=l!3c9~Ho?0Q7x;NiHJnp!2>t)!5a_2V#3r z>lNt7L@+A?eIFs7LahPL<#SC!rt6vC_!^nHlMQR$0)mj^BUc%VXnNROVHAH(-1|>#c zeepNO;_qF4#g4IGGR5}ib3J%98WFys$YvFpzAy6SZAsa#0G) zQm#3bTLBaw{QR*{W`&~fL&bN#T1ho7#xH1h7HmIZQsWWvd6CSDMBhgmdQy&LM(cLHW+!|b`l?2m9i6~?SE^nI9kpViG~cEhRm?W6+`?q|Z7 z6^6cV7Q~n2T2mW8vs%a{JHxXc-tjvSA%8BES)u6rGTMYTPP|`ME00eZHG`ak5bqb_ zm=%ZqPU6fY=U~KpQ5>`4(D!k!amm0AF`rrFOhUYW7ssqP^nILbhtHW!&Sb>Ok?y72#Ra0(ob zIDb%lvx-mO7yqVM{DaG{I5c+iKN7!)=fZgNpB;7t;{As>X2qfJMkl*+vz zj*mSu79F1%vuwFJ60!a)hFLM_*Jsq316Zl?TVwV!2S*{^U&Jvh4t*czc5Jhg!Iw|< z7&u+Ho6b3XG~)eL9JAuk_i?s32y9&rp7UzTwlCtD+Zz$@KgBUC4t*cz9Wl;eXKu=; zBe|%teCBoxBEBk;S&``bNb!sYJ7z~ocIL(%bvi=+O(?TM(f6%v?~Eli*vb|~;pC0I zcPs+_mq2C(qVEI6a~ewT@%)XXHz8t4B(oyX-#H|n!jW_wDI=j2T9N4cNb$}aN>1)9 z?s$+!h-6kI`o0-)hiy{$$NN>(=(>QphyQNoKDRvqsF6aM6^gzusk>sRI?F?dva`4o z!5JlvS#jv^B#xcModnJv;+PeOzK?VF@bj@{wE>(x#W5=meIMtZ;dAUPZU#7`#W5=m zeIMsN!{^vp+)Qxx6347K^nIN7#yIs?228mbb{01apfLiO6^OnM6d$0Wf$S`9Hb8p| zWL6;hK2Uswh6I{5lULp+1GJAoW(A_}+gtCAbzS}SI~SgoaQ&VG)V@NQ6^gzur}*Al zKPRAO*csg^0F4#MtU&a45@@D@<^tsYd6HRy==(s!oes!owt-Fs=ncxqtU&aApy5sj zfacEUVdBjIjTgwQK=gf}_#RvTFu^gJW2DnSnjn%{k?8wK@!2pW(yWSXSLV z{c~R)8wdEhCj{__@m9oslVY3I#?!CA@t$?OvP87vmaV#p=MVEiI!+|BBGLDe;!DLO zby)aKO}kfDO=5R-qgp#a#|v}aN?4u8X5Qhou21-bTrSGFue0N{D{_ggn=M6$<|Qc-V1E!8r?@lf^MB4t*bo%Y_r~ zxANm0b{d=HfSDuADYWV|mJR4MtX^@9XN8Gjxa;IWI8_9*N`!v>L~8CjmG5|RWC{Si zSs=3l(XS7xIWh)fm+`cr3!Kx$Ih|Hr#fSGvqNh`0cVGlggrVo#K_ z%pu&_N%rJnA^4R~&aZ_)Gz(!?y+z;GTk(bFAVdg-i-3JfSjmF(xHHq4>c(4(fJq5s zRv7v|3kzeeGI1WWC)DLZd5)Sj8-wsib3DU7-k+#WSj#= zn;2%rpzmXF?{YGPJerrF^0{E7#V{)deIJ8|Sts5{uHxgta~D)U50rLM%!)$aN9lD4 z*a2tOJn7O8gY%OHoV@p;+WA0q2w_$T`g~@7bR;C*y&|u@zs?pxWOHf!!m^HH7ouSi zuuj3u3P!&^?C>(0JB$6GZUmOc^A-HrA>sJdux{Wo!kHD0z7NOiJ15@X`HJGY@SonB z)15d_hXmCm%MJJLEwWO~cMD~M-SJo-Lf z<*}Y2rKb-I?9BZF;Fbf|C7fB|==*S$Ydzu)i=}5j@>QLl^(GH`Ol}2e3q&(38hsya z*kc!VCUHz|C2$LcGb3u0Cf`o4WE-h-agQ$h64TeK`*0LnR{m=%S-ujAsA@X&Hv zT&e7fxp7Km5926Z2-vxTnH7wFec0erP$@I{Iom(g@DL*tg@@0J zfm|x2St05Bkd;$%CYgvK?VHseWO;F9udW4fnSf>mr0)a1mR_w)nF~N%2jX%O&5B5$ zM7&*Iu_m1Q^4Q9af!gb1bjsM%7WRXx>k)3HXl6yD@0+0U{08689gUqiYovB5NmTqlxr>mENa-k?@MWOGT zBJtIK@O4>~%09>0u>r7Ef|(VJetp>B<0V*Rck2ahBWSBdGbA1%IC4!$l&Q*-O` zGl=j#+W&`#8hR2jAn(aUN^}=Mr(uibLPWi65Wq&jbDQxNCs9R2Z|u(Dz~D z-Bur?e+POkFdKw1D-3-fCcc>0pKcw=#+?0t>2&~Y6v(VV^nIZC@YDz4!nrYb0mkQg zfG!uvtU&aAAiQ0cT314DKLw~InJ+ZXY~;cI2GFh$&8%qjeYAN0O)ZnFPoW$QHy?_8 zHn3>F@<-y^LA+8#vm(;>5i8HP);?)-MC2DC@}%uX5U&!^tcdh|#Q5pOv{;VOlQu`J zfLpQ?WrG4h>%1~66mvEOgj>Ps! zIFk0^?4|w7itASQtXj2p`8on_M#P&0xJ>}wmvUuMz?L~Lk7XY?8Svp6KRqR2RnOX$ z%K_khwp-9(i=voSr_tvTit${`hErZ1%W-w$z0ZcP2IA|b#dN+snQOI0_Et2xLkP1% z(62`90D^nH6YpcALg0UxwYL}BQ=P4awk2rr4z#;d9JAukujbsdX~`Jw^iI6Dpkka( z{IeiZZbPegi(*z3`qh*T*De{uo!v<-igj0!l#5aQcC@-%6tkkxC#Cw{WIUIn9MJAO z*?;v@F8155{6%WqRl~1H6~kXw#P63}h$dSAy+=^9g3|Xv<8#4t+8l*0z;Ezn@a4-C z{xM^!nFslI0{0%_%nC=}hl|f`&p|$l<1hXdl6J*<2RQeNV^$pc^*K{xeQ8&$Q4ap| zGX9HCOFEUqI>i4F-;Mfrg7`iW&5B6hN8~c%#Csb)N`zlP<#(aY{fNh<=OI{z;H+?E z!lMLtruvtR;g0U4RI1#|=F+p5RtDZmcsJ^OS&hwV+v)qZJ-)XNyp@2b1WVq>PO zvk5;Z)P}{OEz@paJG=|1soJJ#vfN6R(MHdL_;xg<0Y5g^w_?q*6=%oA zwqhPF??$+8wiJPGH1r(`7$l9r(H~z zC7^@JfSCP#cKa&Yhx0gHCWn3aK?M36G>fvA?x^%3Z;4zAb$v*FbldULq>{n#`jCy; zkFF2r!%skIkA#xv$`bIS65Wmg`7T|(>?~AfAF};XveRK*OqL~}gGs66){t;q_E@*R zhxf~36EWMoa{Mz>;>*kPC<`L{+)T8|L0u;j7~Ocjrc`$Da3fu(vaXY`=D@#hdAS5{ zanNWv8M(c#ECD|%jOktvSYDR$)@Ll$2}o;$_BpqbcB5?GH!3UX$!Ief4R=SXmxFa6 zl-BKUt}KDFs0=gJCy~~+s#Fsw+ww#u)wzg&9dvi3dNnv)L20?`b7)*y0)AA6nQ8;l z>a99x%tP6xuUAs#qP^#C8rAE;;;7QPQFUbrltraZs@#8~3tm6cdPbu<4(af8a1%#$ z-5z{t2aVHVxggaW)ymSKMH_7>+$1MqN&N38_3~`k1yFKtgfdmy1Tx8w`S?8o^|WG@QSMmBTkcj6ReBOvIKN68Sv{b>n>Shfw{>bRu;bbPLLMe6cbwKaH}zp!Ejp^(?Gh zxVdfo))8;`&~jIdmL>2yD)B+$<6WV!=k{FEyK3Ot`aI4^@6lf9vV9U|+aqi1<7mAR zx;wJvw`zF!pykuF%+{49;74Wn*}jBveC*)xpy{&Rin6En$&4q7XK$Z{-2~ko+474p z%#!wSSZ;$WOTdrH@Uz{H%r;kLJ0XR#v6XC}0OCsM?#PzkkvSAB+NrS2)|DmTM`ifg zW-)93T(#dOknQw?D%pMkhzFp%BingMkH-_*LtvS$D@(wS%J8#oU}lHJXYxFjjjv?; zB_QTNcSp7{{TB?JHR(NhlOhhTW9^?f{H;Ry+&zESoBSQnLL320Q}QF#;%ggs_AKpP&hH4ZR&-{)gjw{WW|q6Q=x5o4%6{}MwBa$(%`8%*qsZ5k*K@RYmr;By zlqcG>ZYH_11R)WLZo`1fsMP~5qc$O}MOrdks#{QYPh^4o9>RYNx;s+k19?0g(7J=4 zD@&j}D#J`Qz>#l^js-5&tw?pu!Ie>c8u332-5sg&8L6YdqUAmTV?N6g(5MVE)$K@Y zbJeIev|?$Dr1}iv^G3_=NR^Kfaa3vDsJgNQ%A+#OR3{*<$Es3&sRa`_a?JZz#Q!LC zccjXvikK>`OVyPnP#%?Grn(hrZL7Mw(~ZOXn8>p9L&Sd+x;s+cs1#`vV8Yl#I!v3d zqcY4?w;OUOSr^jxjn9Ez^U-SyIu*T^!5T>1gMupqsUJoJF zO-$>Ec&b}awkz=AyccAR0k1Q?U z{NXfWT*|H*R+sA&%9YmT>dF#fD&@c29`|Cv8tln+9u}c{w70r(&7!Q~;YzOjb&$^^ z=vDBviD;tmhns6d8fX4hb9f7K?S8b9D}SeB^KQ!Zc;v_>iS`Ispv`Al0veU! z=DHPmZLS*E9Vpuo$u$GuEr9Hfah-u2dGSU&6_&ZWvIP973^&(o8%`Rg#^>#8d$ZNVzn%w@{jk4|sD!Fzc*V_<#cjS5+ za^(6;%kky7y0QfPsMO1qS3c3(!cQWvMauOo1pW&&fwCP>FB(G*KPX%PhaX3~T(KFk zKE+Fu%8m2zX1P1!ahsgDx>3tMro0N}%@23{Ia7Jly1ZRk0`;Q8`0n+9;}34OGbXp2 z?YR!nPJ?BRt}Fq6Fc}bY z;-5#^ysedSf{M0a7HW9C*65WOY z|9}kN-Qc%2mTX+Vyn!1MUBR+QaGM5r3JBYvTR6cluRLVQFO%@GahEicivrz-aL;o> z&Tx`uhd%*z$=|HxX@_EU4glPEelE;TpylNVax0fr2gh4w8Sp=?)U&IWuPLrt zw}j!D^Q{U`>%zOT>hMH6sTF?pI{t=9Z0PzibQ?5u{M?rFbt{JM$q!ZLE1%xyP*HZ( zI@Ad+w~b%@;enQxCy3Fq1YSpl+v8rBN~{?QyKe-)L>$XBz-e*sapCEc%XB+3O*~l1 zl;0L&RLZW-l;1!~U>j+ta*O4&j2abgk9%D|(@n@~`mym$1C;IfXeCpAUx`sEyE;>T z2Z|>Tv^>TzQ&*ON9~EwodtE=%myp$%6XTgSpsYKRDZewtsFYouDZhusOle)Ft}Nlw zlI8ZeqJf!m4o8>Rd6>Q1P6#vI2N~@P-GqHNdu!#fPA;$fvgNK^V!5<_0?H2`fC?T! zrrb{2{i@2Um#KKhV^BL3<~oMo4ZLvq#s&sPtYE7G)4IT}tU7QQCj2|#sZIr+!Bbq4 z9#mjj24WVjtU55?b28w2OAL%f`zTa6K+5<|*w!h(_Cs|Cw=MW)$AnLZ-@$S9|Nq*N zZqs(=X82-8%cA~Jxv!8wLob{^PxVHb(?EjWL{-^c(P`B4NKD)Ba7kjAN`oFoJ zuU~v+ueT3&Ma^V&YgK)O>yVfyuH00=gxC~x^z6td4A3JcuSUd^yu3yaD2n} z$=~4%s*tJt1DO^Y`(l3^8$$T=?i3n+=ZP{aQkIh68Y#OZtQK4*%eQ!Ip2ClrC@h|YfEX%Hze@&!}x2-8nvaH9C-Y6$V z%J{JqWmcq&A8Am|h?Mao9m?ED**pe?G|JLkEuT-iQD~g+ey4$De5{kgXC5gu&gV~j zQE2S5d00N>N}=I1&6JBHW$yjUB4u_98uj`jWt-*mMkIyi2KhHe%8K%PB4zwYnsQX6 z5BPB{<=9BsX<<=thAcx|FaPb4vdiUP87Z41e{Q7ge#L%RmWGcyQ1}=Cg@(V#L%BUt Mk3R!M*&HeRZ%3>>iU0rr literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_picirq.c.sisc b/lab/lab6.si4project/cache/parse/kern_picirq.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..66798f355d57beae653b819dbc1447bce1df39e8 GIT binary patch literal 3189 zcmeHJy=zlZ6u)hwiJCSomKISkgM(E|W2qWJB-Q%02!bCig83#ss%cD8aZo7!1%eKO z4qYlLC|%qg92^{6>ZBk}ia3gc;`R5t_nnvbVj&Icpm^Z$d-vRP&$;L8CW+Nbom46` z1Xi6&4X;&d=}D*IS3vT@>!m1V3(p>O6T8%Z`d2c*+HU~&BcNO0yWj{IZGeWbbYr{} zn;h!u=IT3odluZ&vGgIIA&;#iyc7vkzSIs{xhzsftx%4 zDuG{s@4?_0WD~%Tz&vDB5kCfd8#3REK1P_~>C%7nBYluu)4 z&~q|ftd>VAPTr|rf}gX{Vx3rP)`d6g#|}FmXMY=0(rk;>tr<-@du!FA1-Rf#9X+K`KT4^ z!IbbWr(J6q*BaAt#+CVDveN8<_p>v^tB2{KIO9mgSWQv_)A@Y6omi%e9W{?BPCd@Y zsC#=Z7hwu%jrZZi0s<4uHOXIbo|O`{npmDa0p=O$6G86D_ZX&tYoXv&N<`Lg=<_r*5OKneF)o3G)aP%HWK8Xe77Am zjj|H#!;LO(1~NdA#ch0$D3!e0-1Lpm7*vQ&c^n^H90RkFiOdp>iVX6=^kB;)t*L2= zb~=7)#6{HMz5CNK1r0XhOVn!-jmU5~LPXl3g+JAZtc^cak}` zXJpnX*LUtt*J;$>jBgR#{cXX?QwK}2%%jHS(G;uEsu_RBYX!C}rS znOP8G5`?q(CQG}`p##*1;2#A!*9MBYHjeyNs=pfFiqEr7tRG2g$=T1KPV?_9GWCTKSEK3+1lK7Gcgv8qCTidHRcjoSl zE)qpVMVSJjNKnwE04XR1G>8%;I^387Q9uz1{s068((?Vx&b!;SFE}Y^5Kn&hX5YN| zz8~{u#?S5yg0n%;e@Gnk1;P5BAlUe_&+!kE@U@>d2GL#n=F>gM9l`(fU#S3l|A2T@ z34I{`SbVRT?|w0K`NY!D@bI65jK5dEe6c$Sj!Sp^tCb;iMyaPOKmIYORtosEIliHN znF#KZ#E7^UXU{}0e<&?Z6__I@ zy$8n3?aiio{-)`4M~{c;xY4LL!h>g*lV+&D`A{yy3vr_suY}9-d^K*o5Uw@jM!4Fj zFE!>bHpAv}eQl*0*6OYBv81-JvQ~}5q;>F|&_ECm)pr$8<+E z&Qq~Q-8{)~Z_;=>x;Wpw5RD%>`uqgFc(?d|zaD7?V$8PdDeBPJrAo=2B(K@hi>t{( zl+=<*$@2U5SUdp2V^yWvim~1-R&t-J0q{gfF8swVbnE9ZME#)p(KXqIg45B7p3E zHxL}4>*WZY(bf_b9Zk*5%9N+U(zfFb3cBq*SWH#^+ILd zC{69aWT>OQKv6VNE>E32F*7wAMP*q7_Ml(&ke7g?>*d4>iD{Zz(wUQ;F7yys;(Zt! z(Ic5~aOg5ve!GV>&NY$YV6PWHTmO>9x~l?-3!d=i_dH)Bj>mOoqAFwpnS5S5*vFeOZGF( z@z2K3=&t@gXvuOFP>t=9wd7>#61VT|lBLLyC2U8~By%>t6|YPz4Yhq0-_RT_^NDmf)G|2dg0xmZ{#z|WCG&|{f>L*q zRR!$;p)R!TmKR|yskIIti&~o#KCKq{?&;zgxuJyTu6NG}hl1AGu5GL^1cRU3Z5!;Z zW%BO`ddPHl+%mMssAmfXK!P*o?t!g2=9GMCW4Z9Zyc*9>ccfO_F4>xYvbWOpxwwcK vve>!*hjp0+zb&}9h@Cld%G<1KGuLlsHkOaQ9lUYo#cF|WL=3UH5W2qs+}d#O literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_pmap.c.sisc b/lab/lab6.si4project/cache/parse/kern_pmap.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..98aeddd42367d415d5986aac3b0815cb11f8e83e GIT binary patch literal 51788 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_pmap.h.sisc b/lab/lab6.si4project/cache/parse/kern_pmap.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..e15e322eb5263126bc3b273ca493e01ecf348cec GIT binary patch literal 21284 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 Ak^lez literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_sched.c.sisc b/lab/lab6.si4project/cache/parse/kern_sched.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9eac8688b1758036f0ebe1fe247d0c4f016f1e62 GIT binary patch literal 5175 zcmeH~&u>&!6vrQ>Vr>Tp`4tElCs-N;fsr3VfRZMF)(xo!g~XV)%na>=(l(ueVEhN% zX`%~bG@_A+th#YaSg>=`4X#|M(Sup6|HEFI0tK5JY-Mjgn1vG+t`%i*T!_RGI6C^ zD;?8d?`XYVn`v~6A0Ls3aLkmanHk0lnGw4Q&aG>XF|#gb=Gx(xW#fYQ$gl%=n%OYh z?V*@I=sTyPX23q1n>H@hW+wO6r_;%asmdi;8kLknqXl@HSz^3UH0Blh&e1vn&|jao zRGYjiBj+V}LTDZ3X=a4+qm68kRsf7_$QzlyJUT5aHA!@xc}wVGG0ChjUdW0M7d?S< zo5Dc9@HgeHT%MXt8&fkfbXGzd90Z^^hylhJ^QT1=nB6j8$Q!a_5DazKE9155@c8IN zyhszKgDEE*!tbHHc`cG&fFm{O%kfY-Y*Id zXS~1(lk%Z?e~wM$T(?5o?x*Y>xy?55mGP6VjBDj^WxOd_Rk-HVSaI%)N)g>PrLZ-w z;}TbfNbbsD4#eHYTFc~8u8h~E&ziVWntCwa0X41hpz#&toV9GQ+j(^=t>SbX>~#fu zZeIv?r@Z}KSW>Vzul`lAV5uo+!4gDlNK+5y6cg+$HP}@$G$AC}w?%KUfxr@A{bUrG zjhCGOHl-@`xS_rqe5P%#GeEox!UI`Ns}nUkpJ=xUIY3bBe&a2^Y3qEjPfxuuI@1`| z1(a6LYfd41GsvXSYEOA|2N+KJH>B+%3d!q{fT=9y-|W_ZDdeV9l2F zLWx}W_3^v=xRNuhG&Qbt2qeer&Y+&m>V?0#KXmnN@t&0f1M2F5!nrPVxp_<%9~V2q z*fuc#nLp|XzmY8>v&6q$h%M+gVa$s&D^c!rIfrHdpV^CK7Ek&zxFHZ(n?;qnXuufB&hF*j#3ZbYI-eE=M-33TMR|(dCGJx)0v~D4DfPd1my} ziYqW#519T!cMNjY#nm2>{r9sw?2KgiG@uR`xLzsZ1JzcB{klh0+kBV0*I%kn=nrLU zdPVIFK>_3GzSHZ^p|j~QV!bckom(OQoJ-3qbg|C< zH?dk*TUK!P zrR>gvvRBf+jEu6kjhFpOQeGTCBE(TBNyet}+Al4Mn()A-l@3crh{uJ5dqRi>b~Rpn zY2DXzAW;*XTiYD7&szVC<=(KfVzc#0A&w3TF+l*v%b6&*o3PUwTF?)>)DF2u zm{ZaJ+!Kx=vZ`=I4@~*%tU2}hX$8fl$8il}H*U`4)v@A^y?LL?uZ$I^ziRn}FQnvg z`P8eu*Jo()YwF8ENx*7`_yByUTz36j;cH_#V9S@Y=AY=A9i#Cc3-Mxk`*<<^hshqs zZ{Pc|%&8ak1gx(9;^HFwHPp;>xb$M3vd%;Dd(g3qsN#1DKb(5tWFU!-W^?>n#Ir-W cKjT4qv+>T1vbHZ9^MMS&<3khJ6Nu;i4a#;e#{d8T literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_sched.h.sisc b/lab/lab6.si4project/cache/parse/kern_sched.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..1330bbb2a4d13f50b545bc0f58296e04ca09bc9f GIT binary patch literal 1887 zcmeHHziU%b6uzcutu`?!6pP!nb#aTRAUL*xRzee{RQ1G) zam?D)Ow0tUCW^vHFRb_cSmW2y$f!3=6qvs5nU-gwt9lTdNI#CkZq#}d>$n#V`koHL zO}*#`?f$?s+TUEbhiHs=B8^Xe8pL*R&Fb8}ffKdG!&0(J{;i$Kwv00qw|mBGKJ!iA z^RVX#@N@+A1Y=W1K6+}gg$ma{Pv$`1dA}2Qro*!ib@u^utW(C#br*Zar)*pv$^_QV z%F@+1JCdh_f#-J`@MI+Epfr}zC>s|Q;tbYGJxSCl z7gf_O|N8cpyOmcLlh=0#I$p~|yuRAYc7?W8eOO|98I0xd^??n&KJJP9_0U(Y^r+WI z-`gPiWo7+ih_$N6-r*-Z52&c-`nnU#=O+AS$UfS*gXG{*hnoh7kmfOTsa42e*ixHt$P3f literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_spinlock.c.sisc b/lab/lab6.si4project/cache/parse/kern_spinlock.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..af8777e6ab105770e127e0e7e8a7d6a377687b5b GIT binary patch literal 8508 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 W1<&JEffERrPl$_c#*a-N8~*_(Yk!yk literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_spinlock.h.sisc b/lab/lab6.si4project/cache/parse/kern_spinlock.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..e742deb86d80d5674d3dceb92d68e25cd3abcc29 GIT binary patch literal 5818 zcmeI0O>9(E6vv;X;9#drt$YgV*rKTmWnw@f8bbtB)X*YS5lsecr!b)m4N!w|gB#Z@ zOx(C&LsAn>x-oId!i|ZGZXm|EC?+N@>4NA&z5ajqzVqIl*Om%NSr|`p=G=SkegAtt z?|W@+RgxS|lFp~ZNk@{bS(hZ&zUlCMT@t?X^R+U%E8l(Dja-rZPydw&ko!&IwQ}e) z@fYHXn0cody1eH^F8}g-S*7IXenCIXp`IX; z`y@HXYRR5Ord!&?_nQ4AgK9c|QnE`tlJ2-A@Ph&x5a{O`eo{jW4%mIVZ4wdQfK5`ZUZ=L_U?Om z_|?pfVX4UZYVrNzhr}4(B{m&S8ue7?d`;NgISdE{eCgaovo(HZKWPQcWDp1e%6$(S|H zF{^9O&Xq0mJm4HL3j}s|TTHMVuQrRZ6xk;R&2;1-ruLW=EI-va+iHy0Eh-P_!AHbI z=W#K(c>bukNYmSk`2-uA!*3U$^>6@DVYOcj_S0gp(QOqMsb^O)mqaBLqK`@lc8Wfx zwYX+MhGLobNLa&DBj+qz!SYSbvDRo~tUCWjIIc&q#7?tZaS}RY@g%cCOUf=+a3K6x z+Z^Md_Ktl^3xL@DM8`q^p-j|qI6JC`>oHmsts8>0s`fk>+((-kqbC8K$m z6C>w1CC>ZA-V>FLY2N?R+Q%~Lrt~Q9VGK{9-J ziWBK(FWoBRAg#v*MoO*qG+Z#dmQB_<$L33AmmovtaQw{{R`Tz-yKXx`t72rI80Sn^ zje2By-WP0Mv^b=;qEitZD}CLP5x%$i}p21Zg11d$VA<#O0=8NS--5ah#R>@{6goF7!+g{`VhP(su~} literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_syscall.c.sisc b/lab/lab6.si4project/cache/parse/kern_syscall.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..3ec74837be6e64638fda7322b4896f26e82be705 GIT binary patch literal 27843 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 RIrjQnyvH=xg!Kr%zW@<{w)+48 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_time.c.sisc b/lab/lab6.si4project/cache/parse/kern_time.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..883b6abfd04948713f687ccdfbc8353a59d82e3f GIT binary patch literal 2275 zcmeHGy-yTD6d&#m5INY3!X;3VK!FuVz>r{U)S8a~p);_z9virm+%0q_{s|No7PKVV z(GXh;3mX#}u#kenf>3c4bmQ+gvu}6r0$T7<5MJ_|H}l@SuX%5VTa=nrD)R(TDW$%) zDz(3xGB^Mywh#BSlx=_i(m|e9_jFfYfW7Ylx=_$ZU<<%VsE0rXAmv^co%5M=2lr1g zBNM=fS4HQz;ys{DNQZR%`z%Xa9z_QeLl`eZO;{O6W=U+5Huk1n--d^u;QcWE1-u8A zaHg+1%EdD3I_s46d1>7pX_-s6Uv^j53tr#z6|Yhr3jC6nT7Ka|!wMvl;b9c+FNy+j zL|W&gJ)`8KDA)`wR;ykmK$sb*s3zDDax}-d7g;mTmyy=7miLKkrf23R&5>7NwD>7;2q`yOGb1PGi*qhjJ(u|0-Uha0SJxOpZeUyZg>>;HzEw$lJ@q)Wo7ll`b23yKUSKa1A#U2WXV#yh{z zSZp7D@{fjMH}R7P^Kr2ZM%+fRGC0CnmF%xv>=C-SFmF3M3&-s2DZAYs7t12~ZZ}!% z6#TOhGRwjtie&#TR_4fdHtm;#0oZnhh_yBjV-dX}WG;r$=+BQP9f&U@xq5AS=D|Ek qk}rpA4Y`c}aZN6Ahjk?H?4VblPV61|0sOf_@{X)iNQOsZQvMe&oY}_! literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_time.h.sisc b/lab/lab6.si4project/cache/parse/kern_time.h.sisc new file mode 100644 index 0000000000000000000000000000000000000000..b6718a158d421661c4322d082e1f03cd6a353219 GIT binary patch literal 2540 zcmeHIJ8Tm{5MAsfIAC)LiXtIVtO63yrHD`v4H8%){1OreQ6Nm@yV$2B=jeP!RMeCd z2vN{RK!e=SP*G6OQY50FFeM$XkZ6wgc5mXdWk`WSfq2Sz_GWk9%%V~`0H^aS!8f+L^~L53j2*rlrT z>~^-m@k2EK946YZJ$O-bXcNQ<9_@V3AApgcGKZsOKwg6Lj3Z}BeA7N$C-?}0Q&@Zt zhC0T%gzyKxUxTa!xq~rzDTL8uU0V(|9QPiiRz!VfCzP1nYnqEeE0XMmwA$I2DDH5tAXpSvODSHa2HeD0x4H07>+jT z{xFgXrf@yRsSM~l-WR5LZ4p*(g!Beg{hUzeX0J8CV z4r1lJAdb76%e+Sns}^@j_5xmr56P>sjAbt58($J<-r?HEXKg=8#&q~GAK2 zm2+e}oAra}B%CJ_>CW0Xj78yvp~$(IM(yplh2*iyB<5X>m4lEmT#Q9XOzvA72PgNX fV*VL19_M%i(reyfjrwZW$nB7P(~l1Q3EF-Fq6q%o literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/kern_trap.c.sisc b/lab/lab6.si4project/cache/parse/kern_trap.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..677aa4054340b17b376e3b16d085bf33937d14c1 GIT binary patch literal 22583 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_args.c.sisc b/lab/lab6.si4project/cache/parse/lib_args.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..0941dc7f1baadc39c797e52eca6d089f401085e2 GIT binary patch literal 5940 zcmeI0OKg=z6vsblwHGMdLK>(KUkMwKg$tIb8#cyn@bmwhnRD;lk4p

cV)E(=&5s z=Dg>8bK8DflI%~C#vQ_BMUq@@O_K9ptZ;Zi6g>Xx`DS>>zdgAI+K~JY|CI=k``d*| zd-AF9GoeZ%StV=~0+;p;=cl$btYN%cHL^!o=pN3Wxq3Jc&mawO>6gEnZxNHJt?7t^ zra)OokJXZ$jn9Owj%?F`3&Ni?dxyh8JS57&dxS2lfp5Y(CI*mH zfi!JO8gqBKImR_f@MvkOjKQ}G+ucf%JR*t=c)J1*VsfWG%F0EJcbC5ye6)Y$Na;0A z@Z&Kd@VpQq>xGXC4NP$FCj&XFFxJwvSz?tT0fL)@!A>}#jLZAjOT+og1z+C#CGl@z zw|H&8^}6uRO%LP{f&z@HSKeYfz;gric;Eb0uoAcRm=a0u%BjHY5yd$>ZNJSnDP+rLp$Uao zh=fkPySUJ!2t^S1aG=zJ358dPg!YbSZ^E^fPI5TT`<0&I{M^=%cdZQy$6p@KCqKUK ziizuGMTI!>E>aU`=78;Ej2_F8?m!_u8>unes zEoXflS3UQJ8mHFqwM)K^N4_G(%44~FwYtVHs}?9=yoG&bmj_pM#l!s*S!WYDJGa>< zmQI4REpm4DhiYOGV?kdoX9=|FuquWE=x4mJv(+w)v$>~SKQEW)GI1qO%i*)Z;jg4| ztDxxX!cbaLZ3|Uf9PiKl!J7Zu3sT7UbJ4*Rbv_Hyc<-kIfgeiIxUfA8+^5 z$_tRG@fN<>M9%}d8|mYAwReeehY)(VkN_I55Y_X=53@HWBeY&A&nk*DA+uJ>vx?&B zkXhcSLqKbKdh_4G-`UdR(!%7$`bY-NMv z^~!Ro9|tR&5h+_4Q>1%@ z2L)l4b zqt>QNvgu;RrZY-RZe!IZa*6zZ%cgeepV?9SG3^rX?5e6w*h0*f%cgrJyEUN2R#SJM zi9NV4pw)Vx{>OFV=QTVsvS3@X{CIvNdu;#B?6osDORuM+9zW*AV{~Tc6+4I_F$OHw zpGb literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_console.c.sisc b/lab/lab6.si4project/cache/parse/lib_console.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..cb49693ffb87ed8029cf72dbf5d2f496191603b5 GIT binary patch literal 12847 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 O&gW~Iqj#KvU*!wAd~l@z literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_fd.c.sisc b/lab/lab6.si4project/cache/parse/lib_fd.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..2369da206cca2052c3a535d55be3b8bc633e8886 GIT binary patch literal 24178 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 SDnMliwhOJk{50mcKmP^3PcNMS literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/cache/parse/lib_fork.c.sisc b/lab/lab6.si4project/cache/parse/lib_fork.c.sisc similarity index 51% rename from lab/Untitled Project.si4project/cache/parse/lib_fork.c.sisc rename to lab/lab6.si4project/cache/parse/lib_fork.c.sisc index 22decab939909a53b6bd5b4aee2c4707ef530b1a..bf559327f94bd7656c49cbacadf9be482c52daef 100644 GIT binary patch delta 1272 zcmZ8hUr19?7(bhL?rPWWbxybGbi*gbs2g5u{exKO* zDMX(TN;^!rg@}aS`F!qh`tY#c>#L{{7*p7OUm=8vz1ZBCF5_;0dwzLc162IQpc4oD z3PW{f7iO=VuJ6dsxv#qv!&=j1lhUVTxVV8uGKR$m<)o7A#3Jfx?P17jnBs)U{^S zw~tyg)W5+7s-li%4^`toWqwr&a3Fp z&Mwr33M~K_`!CTn63sc?NFWtrT{Zy|@@|7+CE(cBXeW9V4zsWs1T+?Hl?bLfl(z(C zE4Ktp)j*^1-O6z-DklQrQZ5-pz2`&)(5^P3#?ym3m81%kj)Sw4i%ls5$4N(sQ#hm$ zIqYR#l_`-5#n?jWSm<&m{N|l*!QaRh5oD6H<;O)FYgeor9@o76;N-K$h#E(8EsY=7 hA9o+f{%O4LR?~dh-_EP@QFB<$<74AHKC;a#{{Tz*AejIF delta 1704 zcmZuxOGs2v7``*zvC%tMZz?*Z(Jcy$TugQe=`mQDsXih0vd^A7&bIzRm|KIuk*Lk#FY$^>E zNK(5b$*Z7}SCVcleq37Wt&SI(YAE}(P=fT~_}KzUN)?%Ec1yb=8ocG3Y96jp;41xv z(5U%rOog2#erJ7xbs-eKmYm1$Fw~RSTsb!4J?oLj_lFfxCAZ5LyRkWkP4W~rALLzf z{ZVXm-}04oErP}Y&ou-Zv8{b7&ppn0GZ4ks;=ndiqLB=xq!35un;pkAtS^wGVye;O zXH+_0JQP-FOC?o?*er5N&kl!J%KVd0Xoy9HeyV*-AW?>5!}*mgnF0aNwA?@$QK zV7L;CJcuQh76!$g;6@SmpE5>a9CrYgA}RgQL4azjw<|6g9~>zz831JE$zJfND%ve) zPllB-v#1rB>%q};<)}Z|5w@H~UxhAO0*!L^0KOywYm79YN1f}UfF$1+pfKg|WzNZ$ zSF_M___nIuMg+#GA9ld|U_gKjKqueY0TY0=CFlPD)))5}5&$B7R3*L#nAJXF0Re7{Y(f893|noEzXg^=js3I#K31*NZkyJ1xO@ zV%om_GJ>RPyxkYg)rv)C^4wZ+b>|^_CT4XgjwFRYj<7kIGs-pSEvS$4@L6l~%JnSt zy0X3U}k( z=YXR|%IsItvg+?-zcGVY2j@9=7MtqGEN5n)wRfEvI|WBljTuu#iKhD)20n=8RPQrPI%eYbJg z?@$m;6(ML z7&+&pqaPm!@})z^vlh)>$@{D^9fpqUq+fP9_W*?yMp32Uo0)Pl4J@gfj@#XM>RZD~WP_C{%#GcCjp=iHiYVz0~C9!#f=v4wD wT(u$*x#w)FxsDAjzUrwBO~=&9opsh!5UU)@KU}G7sZnIZzq?KmJF3(F0Ow9s%K!iX diff --git a/lab/lab6.si4project/cache/parse/lib_fprintf.c.sisc b/lab/lab6.si4project/cache/parse/lib_fprintf.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..0fe9038dad27dc06b21196893de13c04198291fe GIT binary patch literal 7761 zcmeI0O>A7%6~|xfIB}@MxDcfw0WnBGQUqigl8}B7rI4su1w&Icg+&~HTQy>KW_uf!ds8W%tO3-eSMcYV7ef)p#-v4`d-i*f($(yQ2 zI&Q`61wXmD_0eqvzm_z%R#26P5-26FujwT&8P?PIq_poToR<-z9R z_Tv1-rHSPk1aqkLErjhAMetXIUlkf`;Pr#IMm%N?!Gqb3Vi|IVf3i5gG%dj|>VXK} zFGRaXglr_T4+ss`dgU9pMm%ON&xf==XpP{Z_VUu?9O@1U_X~j|LZp%i2ZRQ805^}+ zr4DO7#zj9|o+&&rqeRTqi?XMg5j-nIFkU<+G&rNy4&5q}tmNkyS*g1{%Kg5$%$$r1 zpBJ^wAEY%h`rK?|?*33C*ChWnxpM8hvyCesEi^c8^qavC zco=o45s{7>;ThmS1LxS68`a2J9rWM05wqVF+FT4k7L@bR$-Fep#O}~3&#Rn3kAaz~ zF^G_yLnIVYh%~P*Qd1@5xeW@Y4bD$pL0!1G6d6!b3Yq5BWin^T^U7(+*|s=+X?bDE zH9)?hDim>SHLotVvNzO#mKK26q3OlNR~K;r(g+~OLg_C^HLosJQzdjcK-FsF-E+t2 zuX{s24N36NgjbbMxBq#5m~+Ud!cdK!Pw>;e<^wI0XZC==M)^dolS9A@!s1*}&U3rc zupA%GGF~Gkq`KsJr3L42z5MF@)ENzMH_oE(L3RHq4l=Ll;uHV+;d*VNQUHW*nRrRR zjA-c0>D5-YZSx*XB#yP}5{tK-b_F>*=VzJ|PaH48e!PW5(#NKJtRVe72I)uQPYG8P zq!X)?4eX*ItRYb7W}lfd=dpsro1lcw;84E-IJbN`)eXKW z<39OX$jTpTVPUYGF#hYxU<9ylGYzI)a0cl{K%fVODNhwzS*5v7gL}3MiaL$&37K~6 zzF_X}B>Qy*XHyu zyzxp2sZKqMkv9d(b2q>VP`VjDHLscY^k!G-R4I&c1Cw*KUxsW)87LKLk?@*{$Whgc zSS4@;F(7AXX>R%AkLenCTy>n?6RCr5UOo7nXvp*G38`BqUe+%sJ;amnVIjtFig_Jf zLzfy&m5}Gv^%9A9hpwUb+B44_I&u_|XUsbjid&S>OTVIfT+v-$= z0#48Vwvr41(6@Mh@t6ko#HrcF8?j$7hq2=)XB%s=JrY=qB)4oVbqR>o0Vtb-T^&}3 z-*nz%eO|KQu4Wl9nfmtd(3Ml{buE8y^d=wEL?VRJ=@6>Xj5pB@bo+^ zQb6nJx=05uw8FueR|+-BCS*Mp>Eoi739p&RT$4=Z3|)8f3vGHL5o9ZGd?>wKVe82u~C}jW9OLJRGdgf8(<)EQmv{7y&&v}FM z9An?v%v0Xysh zblo~RcVV#}*cAo#O6<_*B$&W{EZ$mt8xdHG*-T?eDs;mzQr1+%a@^lo+E)Mqv;Fdg z^9%Erme4s(E z{s3Z%Y>#FCE}jjkPW>7pA5gflK)I@5k9p0cEsS>k!Kz9X+v2}}hHR(EpxnAF5?(VA z+0u6!u}bJtGiiw5(klM_L-~7-O!MU4VLg_>oS{cUq)iZ!Zzq)fY%{MeQd1>#sX5tw zw`nSC&c#e3d#RkO;x*q2ZGul!_zy&v&1w^FiH@kLjx3|xzjB$k+4>IkQ6EwL?L z`|ZkBq=1`gt6O54e(v?=^|#f%ENO|b(iYKD5dR#FeO?*!Z{ezVdp@oH_t6?KcB2-7 zGYp(#S8voJu4yf8g2VzGl?E#r2=P4myN1(ff6p4Awr0&{J zez97{#_tKKWvFY}Jo%)fmbIjWb!r*cofc|qL0!w{^%rTU2lo&5sO9Sl?~Qm(_ACGQ z%I}qHA;0-7>{>>b&8lVRx7}~5Bg>F{>HL0Jn@J3?az%(Uu?9I9#5iD8pL$k43LmKh zXWd_KS$BaZ(lzOLy*<6w^y0C9J^p&K>-;;}|E2ffV;84mK{97bvhjbMFY3W-cu3c* z>4U+_U(fJYro0L~$3p~q5a6#6B-Qr NJbDJOH<02U@Hro_N$das literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_ipc.c.sisc b/lab/lab6.si4project/cache/parse/lib_ipc.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..a3de63d44febe7a28defcb59bb1ea55dd0bf61ad GIT binary patch literal 6019 zcmeI0O>9(E6vrQJr6U#w<*SO(CsETPNCh>>hbe(ZOr%t)Qe-i;ooSf_+GbiB+!zedpeH z?)jf{?zwN;wK@p;gP^5T95e;Nu1!I32Mub^rcXo0bJU+MHgA z>8?aF&puX5_OAa!rM&eh?3ev0-*&xjxk~w@&PnZ##-Ml&A1vLvd{Q?D>2l zJfOj@v&EsNGta*&J$7{@V(QRe>=LWZvBv!|4A(9VRmCGCjB-lEnoMzGI9DnRPh%Lx zZZY(N7{eIdBQ_#H(B5~3GRzxA(F%qq3uF0HTER;%B`cNP*znzw#}XaFjO~4YC}R;s zA4P3K+{!R^unmt#ievd)VQiYz`DL-F64?8MD~qeLxJ8!2Q={3zZH3s|U*$E%VVt5Y zkbWBU7?ZkCnb^ldTCXeylDF1q|En4oBn`Bs%_K{0{Ittr@^+<9QRD)^B!09O$ENn| z&P|=x7%XJzzC?zS1k-eSE|Nt2vD2iC=2-B-dJp1MKw#sWMDKFBTXR*p_W^OnOQcjy8#uLhmB`cMe ztz({j*b%B%UaYZlvJ{(S&c-UI2YEK~eOOjb%6KuKo2tg!C2Y5nE=z*|@9e;6cH#3B zFMh*~#QU=B;~Utl4v~=QYU8Ek9N}xr^ms8}4R&6@j_!25g8&0|PzfLOz+#5PH4>F4+AxH?YjBA6nQgVrDRS4j8?tt5F|XZI#@izGgn zF7*1tg%7H`S$ZV^a`n_Wt&)84h>2~9d`!~Jq6xW-gMJ%6i2wv*j*swP%t!8`nPQc;|?HTW{0F7lo~TyTi-Z zSYEvTb#?KtQ@p%SxEqO=18&u}O=B=3Asg4mo7NHjX3i&HLDdPK7r0DvO|vP0XB33+ zixe=)C3%emY+Vd+c8ZaZ>1qSUOGf}+@!IMW*jq^}@7E&0E=t$+RSK5O5~N0gwQCDt z)?bj2>1u-oq9aCJTO`OoxuI~iCs*{(n%gf>HZg@g)F!QT#Cw~HBVp;_!JR$17yAz9dXM+#Ug!#2TM?AF}m+WZX>p*ROzCe0tLLGFISImwcN4CiN)> zB7LKL!gt@N@bqZ*LQ<9dbD>#aZ_1~+i!E!x&Bqi6nK6?4gz-w4nKxpee-o!GTF-q( z7r?&#;x;K&S*Me@k!K^{m)re1pq&xZV`6?!As-U6m+3<9YF<)(XsS$6l%fMy%ha0_ z1->A7;NIuxAngmnbkbKCVnd^4Ul2uR{q)Qz6$8nS7`yK0RYnU}k_MUl!vSb`(&+2n zLS+&pHm=;y>DyaL=g<4&x@(EKoiED`)g37AXZ{?{GPE^RyT4{oYi9p&YwYNfJ0rU` l^zZR9*>9qwWsUn<=CLQy>6(bI3?7eoIxZ{dkP`vx{sBA)r9}V$ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_libmain.c.sisc b/lab/lab6.si4project/cache/parse/lib_libmain.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..ac4aa7f8b0bd5c1649cdef43e3c16d4ec6491270 GIT binary patch literal 3197 zcmeH}%S%*o6vu!0NU^c06tNdvGDs`PMazi(gDeV*HqMKg0iDWJY7;Ihf(lx+C}`0} zs8()*XkFkxU5e47%ykX?v}dY8Tb*be)*M=h~v}l_y#m?0mw|?$7acNlRA-TK=z>^m=gUU zS`Y2Wwz>R0IYRcnRG*<#5%qr<7mYQC*<_M-U2PEEQZIxLE9 zco`vuvHaHWu8ZXupQ^1{p?;{lgR=Z{@)mx?7 z+_=tkjSz?odQ^z6@tWJE?;&8fCtt1Qrtj2pQzb%1)dUXcjM=?Hy%}||LJU4h7otn@ z1Qn&O`cjI(jd(U}PQ$hu4I9$~-q5glFMek5xAG|8jSu9Bhhc-CY^**5sN2*Ren6nr zuw9nS+d$H}3!>P^7ezO!WBq*ExqiM4lCk)H90mS6=B`(Z7$pge4MfxmYCJD&nQonX zem`Bzl+oODxqwkTU|=Ys$3!i9cwWNj?Z>T+&Y_%q3r5wR^%LP+z(^E@i@Xm5=d)zQ z{M!f+4DAuwustTnJ${e}TD1q6F`*@96@&s-LAB*j$O->8w#B?fqfxERq;qvw%h|!> zVXlk>xq9L)^~AYi#bV#8T$3X>B!AXm9qZ@jxu&M2P{~abctc~A*y|`++q-k(%`e1z zhhrPt?be(J02{?5~?4Jpo7Nq5-YOup!JH>_YMR4DGiJs zP8fyfC5*m**xKk{dpEBqaQI~7-aQp>r8k^Ko?F`|TDw)VXnSYXGAfrr;#9TyED~J^ zoZ2_oyG7--8s%zx=dLOLY@92)+qHMe#?nl{c+GQ7jUan>RLTr37?&bUhJ-*~Ezs1z zsUY+D-{Fs^_?rY%-=z4)V#GtNfW^%_@JcaNz8A9aO~xp4$gD|b=|?DIJIlX227dsM CEKZ{U literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_malloc.c.sisc b/lab/lab6.si4project/cache/parse/lib_malloc.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..884505ae07ab4a225d0253c1df5dfe74983c1eff GIT binary patch literal 7110 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_pageref.c.sisc b/lab/lab6.si4project/cache/parse/lib_pageref.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..d1ba28d005e046e9bdd5d6b2ccd8eec289faaf3a GIT binary patch literal 1735 zcmeH{ziSjx5XUE%GZ*FJ29idxNMYd*f)Y$8AyI2NLBaC$?pzOI43fnB85RmD0!b6m zKfun?BBf14#KKw-5)=e2p5M>goy{%^rVbhS?#!DXZ|2PmdwIz0wpsnWU^TPv$IN!# z)FM8JgRRdyO>|rDUXQ>B?T8Lb2bldSVO$S-F02b$13Mwqg-F-ZWccFX2=ZIu^nlr> zbgM&mhoe3Jgq)sz`q`9=adbO(LcTlL(fQe9%Q5(usjEXRaA&eDP za*y>FI`hlj-o#Z2m*)HTYY%7ClUSSA%)`02DR{jSi#pyTbJYMHP`ubA- zytyTKE{QQG9P(aVF&ob`*|$8e5Ik#MLw{lEW*&=$xxf>FJg%ECEB2_RH_KE|2zX|M z|G)}WBI!qEjhxd}{7SmwMyaC>4*gZ-;!=;;@?=IenU5E;$=s2R&D)i(z0pbN1QUk? zYGE>Bm5t{NdFPP*)yM~#%&k(4ch9%myQhhq;XbG8ME3Ur|N6?~e)p^dW(ZI^QJjM$ zz{6*?mfJsmS4LAZOZ-1^x9^VUXJDAmi5XBeU1OpBilt=^wl94D90g8!IB4oa5f)b>3B-oB~k}(kqw6v5IloT#V z1nH@0C@3i1gbF0XUyxL!Ksw&{g;0w1JwSkFd;&(g*SrQz?~2ZLMXSI-j~byQN|yI z$s^8nWa}JTQn#cM#8UnKtE4TCsjcD@vEE@Fo6YTP_6+;8QrBcz;4DBwJ(k*-p9)vf zPu6D;j3*1+J?^(_t!MQ{`ML%(E&ow&?Z$%a#Af6sHOB90;kaO8ngK*CBk5gCaqEj(#jmsD^7%a zhE3~&oIlWhsBFg`?n`s0(KpSqjpv-BV-h)-;-qoHI1107_;s8@g8Wed^cjJR%vMhQ zyRo_coc1|g^;->`!W0J=VtrBCoMJ0yoOYE_nGy+tKBq0e-I&)hW}ME%dRE$;Vk>8y zzPh#-cDu}>1c4<#3NF9;c;lW{D9cF!zi5C@vuWM3D{HhC4)h5Eau*5Sb1Ul{&34o7 zou>EhYSc|sv@TR-t9EK0hiOtY=Lls`H>Hy8(GHVqwy^dt&b20c^I){!|eU*9^xQQ#Ns8{UHt}&KcZM{k7@R?r^m1IXSBy&xPG~b`~e-;^ArF8 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_pgfault.c.sisc b/lab/lab6.si4project/cache/parse/lib_pgfault.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..af5610157f14b1de9c8cd42d7c8c15466e9322a5 GIT binary patch literal 2569 zcmeH|J7`ov6ow}cO_0Q0iNr(&S5!1An8HHP2x?=aAVjcGve`#A3zryTRw7sgEA1^T zYy(vwMRZMT;#X=LT&<^Z;pjn{+d9`_&rN(Y$zsARtidM>6PqBwUk@GTFYHO z-80DgS(kG+#FM;iYd@kKHbb1^>DIRaIKrpx>4?X!R4a_X$GBu?!!xOCrZ%*yNKWW* zQJNT{uZP$9mW{o=>7J=Db{r3x+4Fg*K39$!m1^OV7U!aRseAVFU3riNP`QHOL{#m#Br-V)R~)_ugy$1qQ$Ake0e&G?#YIo7{o`9ONeha>z7O(>>1)% z1*afRK8rs)Jy(gU3q=_j<^c&fDIvf<$%w>YL0>GF`fbe~P3rPxCW#I`s4jNifqzJr z?JKq1owEs9ZflQ$IgzEfTF&9Sv{dS+sR+427O6I}@C}SKtT{`O;7`bMOMaG{4G#@$ zpy!=~_Ziv=LUswbxA)=wdIeKhY*U53N?*o|ZW~p`+p@VcRo_U8<%|y?O|c{uUZhWt zaME^$jo%eZlX+&VI15&|4c#<*w*KqFCv})yGW-BTOiX{w9vSN+KGPR*Kzr@U?!2HAHlAYxDxD<~U zu`}!t&*YTW2swadJO)pXCV$rT_o{ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_pipe.c.sisc b/lab/lab6.si4project/cache/parse/lib_pipe.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..bee4c6213952eec63275c5a7239ab7c8708de287 GIT binary patch literal 16619 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 JpCj?S{|1gkYy$uQ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_printf.c.sisc b/lab/lab6.si4project/cache/parse/lib_printf.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..aa93d18671775f2374da046e181068fb7ba6a2e2 GIT binary patch literal 5213 zcmeH~PiWj#5XXO;CT7*d#5S!_6xW(!YK={5TViQKL9MxHt5k@JG}+z!S&ZqLY=XUc z?9qc@4uYpb1P{G<_uwH154lt#cqmk%$B1Y_g`eNgdvA8%Zo{TQ(}OrLJHI#Yz3yJXb~wU)sU0H4A0w}+2{80gS-3c%*>VYvhkMmZDQRj zE;Tp?Zmi=#jmAFAxH7YGnz|!)Lu_Qr3#Iu=erftm4Rm!Ss$BbidvJi;%1f2PEbdY{ePZaS7*xvnkl4sjS0^)_gkt2~t)XU@3(nzbwV5+E z6jq*=G~i>lThjU^Th4S6o>7|ORt>m4;Nm(VTWDw0mi^E9R{cwQ_#5eZSAOfj-dfQ+ zjMJcP)v8iEGE4^8fuZeMbyaJK7dkJ-E|r2!Bz9xn!y3m_-gpERvi2|lzg zUn&*P=>f;-L+eL62t%gJ`52{1lG&-orf0V&KwAp4uS@XA_9uhPNg5su}-K}#MS9WL|v7EK#~B;dr`)Vr zOU_psV@(v-#(z7MBgB0-3IPtkwLx@~ggn0Em-&L&4n& zz0Di}m!%s!RZA{`T7{8ZOBU;6!r_?gplp4djCdXRu}>e%{@U|C39kwCSMjEF$<6WV zeZyPD-Yocp?XB?t$$j&uFe!M*im%TPt1nuHrc*+C!n6~yKX=^S`WgU}b3G*Hu15BX ziN$oesFTNL8avreI282s2NkX`wCq0^B>ovDx?F_0cCir?k350XGFj#dBSp`Md6gjf zECSMWZ4so@b(E&8^aNxk&p1x_pk5}u&-#Q@`9<*==~j;WBIIiYw_rTktsBN(Dt7oL zWwM%(=3R$(o`|%yFgv->80>kyO;^JeGpW{uVOYL9}#oLrprZba%%_OEMMH8D&&J=FiqDMVM<*`X?jODbLo(x6Jl+3l4A$b zbj=~HIK!o*k5Kl;DIa`Q7kKz$uzpUY+jud&RP-m|UiPycze%}UO^J@N$rl4B<*C}w zeH!-!t{sIxKn?;P4Q$)j)dMSEO>{~9Z<4`ph1odQIF9!;n~T@Q&pY13xo~cfogW2w zO=q+IbHSaBtwA5J>DJqS_$R-{`f~)JKzoCv-yn*Me*w_bUbtVrnW3k=a9`wo;dwAf F#NP_h4J-fv literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_printfmt.c.sisc b/lab/lab6.si4project/cache/parse/lib_printfmt.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..549ed4f4e1783dcdd5c5a8aa6becf1a4b7ae6463 GIT binary patch literal 21884 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 DP6~lZ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_readline.c.sisc b/lab/lab6.si4project/cache/parse/lib_readline.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..05af95e58cb439d8478feb0300094adcba2af617 GIT binary patch literal 3987 zcmeH}&ud&&6vtnuY1-<@0;>q6rGBqfk|&#L-8*0UE%>TwD8<=;nzbu)frLL-VsHI)h8bbTX;9#17_q>n*RCsJbRhinzndS zyLpls@5g${_J(^>RWHW0;YW>XDR@EiC5^GuMZ+1!+C5#1-7z^g(pe>rkw&w!a=scD zXEZo;_FT2GnyVkh3(=b=FH?7y0 zRrCj+U3}sA-28%u90iSLB=pl!mu-zP2c3_L#$i8e{Jf@oupZ7_>3E ziR%tIw@0x&ZP#~yq?;Dx@J+SBm*OwLBq_vI;jc&%j}^qEpbmBJG_;+?{sn?}|N8*C4+wlR0?0tu*y^(YN zY4ua$9ct8yFTQ;8t(WFb%+DRCyQraAD0sch>Q>gXe&WT;W>_DHSI)*KG~kf#O^s1r z2xDMd8D+DPb6Eo5yW(cGQDawlfxp#9VtOjMz>8X|ird20KCTofEH8ugIMHx{CAvNw z%%Sig@MFJN!Dzv3%Z}V(Y63ag)3CPm_H$H83gP-)YPvLa})A@x=q;k1N2!j^9wnhw=1%Rn(c4 znQ}ca$GOQw$1E;Vj7&Y1Cwku|v=SnmQJI~n%+8kHSSnq-=m+mSRVz)7=W@9vt)qhv z?2e^#N@Q4=Lc7lwS34o>jF7_7*;27IKHiu~zLJkmoz@Bmigmn9h|%W23=6%iKiT^< z?2~3f?AwPI5^7pPtrDwY!<5w=ucZyc8n@#aVdYOM1#IyAaCk7W(JAJ9X5EJ>rcPge^@_)75 z9n-M>nrwWQ)rnZB)xQ zH4b$A(DY2&`t8nbUq_M?xk8pVufUpeif&YVPNu`t6YAX3vr+xu~4 o-upQRv$y16x|bKo-m+qV@!&%CNkMPSBgx+mhF~ysAQ684Cn4gHYXATM literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_sockets.c.sisc b/lab/lab6.si4project/cache/parse/lib_sockets.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9153643ad72c40132532beaab4d0f58a801aa647 GIT binary patch literal 20056 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_spawn.c.sisc b/lab/lab6.si4project/cache/parse/lib_spawn.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..a16e5da3bc8804349253361f88f5aeecb4c6bdd7 GIT binary patch literal 19272 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_syscall.c.sisc b/lab/lab6.si4project/cache/parse/lib_syscall.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..230b041f388202e2b627fda2d113f4138a3396ab GIT binary patch literal 19327 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 JwC{{`{|lXOBm4jW literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/lib_wait.c.sisc b/lab/lab6.si4project/cache/parse/lib_wait.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..47a98cbcb7ac22a8fc18afb829ef847fd6611a2a GIT binary patch literal 1734 zcmeH{KTi~45XOh&kc+ujc@$u?YbR#MTdCMJQ+} zj13Ki9fbv70I;yo4`3oOYC+8M{C02FT??)!JjrDKzBBJTGrP@OW-YVwl*meEzlY3D z4oeB0ibMNfPO8}UKfSNP2ki#er31|VzUZzV^iH%Z(i+%pQCS3jJnaUbTZ1*^kD{>w zvxaP|wRQ!YBktkr*`KOH4Ap()1im}?vAOPN^Jj?9Bb2fv;%ou+e>Qw~3eMs^;&?Rk zQhPL7Us-z{b{ex19aj`z|5fO#l)-cPqs6PFFC-pn(fuuv7_3bEJ zkc6gRmyP%Mgg9BAwzK}=@#Oo?r_q-ms<}cGfBwrBQdE(J&1Yv z%p}@dE$sZyi^8U37r07akdu1AYct&g^|kVCCBW`6&WR)Dh(^b literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/net_input.c.sisc b/lab/lab6.si4project/cache/parse/net_input.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..fe86c56a68a0487b4fa506aec1249be354f4d98e GIT binary patch literal 3880 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 Q;Gcv5@OU-@+5+DH0NrL$V*mgE literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/net_testinput.c.sisc b/lab/lab6.si4project/cache/parse/net_testinput.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..91abeefb312b5800843b9595a11aebc1078f63b2 GIT binary patch literal 6922 zcmeI0&u>&!6vrQ(mR6b4f*|-KIsuI+3R5YF{Lo4T*$4%S))*~gJ8hrR=`fxCFm7~X zTz26C6BZ_hD8vN|P51*`;I13g1q+tCFrf=0M#J;_dGDQ>J8$X~V#>mJk~8PNbMO7$ zbM8Io-1jEC()0Q~ulY%_*W`ImZS=gW-!_H(gCuA$iAoc#mh zb#mxa@i}pu_+D|d7`o9r+Ohj&s-5;V#mM&xfb7B1j^eum46RTQ)Ab*JPq#|TqjhRp zNcY5Yg(QEBcv%o^&f>gZct(^+d@5LA1y zX|-P%Iz2U<&ul78HjQn{OU3H8L?~l;+Ka(UU7;XQUd06bMuXp1$d~<*?fLRpu2gIq zd*KarG63#~t@Z zHXO#7*0KET@YHzmxY|VZfw0H(N|NAqiF;!m!ZvmOxrH?SMnTXb$DRlu!EKou&-n$m zz|F_`mq$B(el5z!KQw(taGlb9{@JMx&^n)ANHYh*8uF3$V!6e(8ZpN_UMe5YYoS{j z&k2CS7&`vGUj!62aRexGvlvj*B|+&ks`Gwy#xEXG zFPXF6ksK8hO_u~!SA~M0x&$7tD6yX)ko%a0+#_b3>5_=m9VC#dPqb31U7U*>q^A^+ zv;9$!wrJ3_I4j-ZJyFOLqy!&pT9AU}27?|$0@sLj7zZsCq+bgBYmNI;@(TsfNy!!B zqqIW=&$EEFQecv9Yj;Q66wj!&vF^wS*b^E`z*0-tU?dx>wJW-e6R>l;lJ&*%H*$7m zv6LV2XDw!d+Y&=PEordmG7jve?j&p?W`}d-9KnDZ&}U=0Q_?`uWgO7Hx>{(*5!sqL zoi7~G96~nhNDEohB?FB0jOs$xw~+195Rj1VVrH4HF=E2XccwsR)J^xOb3$3&Dq5pL zL4XAju?uTPCQ2E9e|E3GPiM8)-?|m+YLZuPe4m_%l9xmo7GGELZcHETU}f$@BpzZ} zqZ`Kcj&tttNk50BmR%34wedo|EqKzMJ8jAlD-GgUmvbS=hE1e)VNMTH|(YBFHa_|B_P!Vtvwk z$Hh2sF?Mu=z>eve#{*)>q}$dRtqVKjAJ@*h>?0rn)mq`}FdNe=UM*%J+UPP)F}t*V zi90M;8m+v{0l>!x(!QEZ=Yke@EDbu%HjxSk#T)^0lNd`)r!?V|Dny?VUmr#(HQEZ3 z(P!8ZJ!qnL$dPtCnl1^ju1=7HIo=*HgSgJpbZgS5|Pa^ z;!DN|=UYbn(qws1E$n+$yi!aRz%yo=&b`Sx^#W7t`C5&P2-LOyNXGrZ-M>F;mnEmi z5`6R85~g#PnCnV@GbS!eE#Z9TYK49w5~Xj6*^V<}R#-1Kox4I`Hn{>D8xc;l&W!n| z^O-KNz_o2Gsw|(4|8`-`VeyBQuMJW8;^Waahk46&30tY|)f%j0Qq4-cyWu5_ml4rQ zsZ=L@y@X$JspgdMb?%U^rH-nv%(Z#T<*SuEe6&J>giP1iS3AAvT50BmgURl}X5`rA z`1_qcoINGoPwiD_$r3Wi4wkwKNmWB^h)Zk|6pfC4BTfCUYg3Qb-<`NCv#%%sodckv z;NEiCgzc_P^8`Tr7J5s4INyhz=Y9(Vp9Ek$c9~7=$71_bJ(l==LF1sM_K9G;7>=yo zhm;uBL)z74x@1jsZK=BtXT~PR?LHh781)JXIF6Vu397CN)qxiCrEyZ0-OPyrL)srE zrfUppaTM=XDSzfMVXTX}+8Y^)vG8Qfw%gZL!sfrZ8UFjEuG=A-dOKt9#+XL-H0o<) z+nvl?YV5!5Z)E2`_How40%S2kwd&NTl=sUJ|1F2{Kp&T#JH}I~zw&D|hRlWd6T^r< tU7(jD5&m|^?VeaZDtRDo6LUsnE5yI(5aW2SfJih59oIAzmS#zGe*>PKw;})l literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/net_testoutput.c.sisc b/lab/lab6.si4project/cache/parse/net_testoutput.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..6ecb7f62e28a529e56af78d738e09fd7ea1eb815 GIT binary patch literal 3402 zcmeH}J!n%=6vv-wt5u^3BGyj~MF(9BD2gAQ{J^QLHbF(CmZoVRY8q=>9bE(`ad2>O z6|_?q_YQ(0B3-JBgKjQ@nOubH|M%WIdDj>%4ld$>!@2L=bIxs7Fqz0d9-8`Q}6po`Fl?!@I9pczoKOo<-1|&=sQCv0k#b{+mQJBKxG^1&;&^ z!W}vfi(VJKD4Ja9lt4b^akt+13H9wbm)_#f`mi>7b*3^_=&jwzO!Qt73-`^{h$iYe z6OyLrm*AX)6F@#+IyqDtD3=DyrQzcP<^IwIF}Xkq359MG;=EzMZ0}D{LwHFIqwtW|U(T$41rcnS}i7D^uiYf;Y55nl|v?Fp;3%2>6~ znr^(O_3W)hkA8UV?1iZj6 z^bj%LFf>HzH>N%#SZxPU>{w=&8=A6Y=Y+#jd|pPs37N<_91xrlaJ!R3b9ogd}s(w{z(g&UM#r#532g4{FOW8>P-XHeRb@ zQfA;>Y6itCLNwYcz$jX9*)+oYBaKrmPx@|?5Y0>@K9CUOX`Y)A<_|ZrX<_Ko4U&uj zA2)>6g1>kA`ii00yr)n+-6g~XU12=m#JoBIRR+#&w_{ZN?bXStLfBV48Xjwz=}aa= zCj9wZJU*U#SdQnfq`QhRe{wu`H#?BS34bTyWV;_Nu_N$ngB^2nx%uNU`Cja2*}XUwd{c00n_bZe}a734ici0=Uli;_QdsT?c3!+EYtzEodbeP$P{{j0LVU1Po4j8LUyifyQ=nwu zO8y~nhfj6+)ZLT%gfBGG-|}gfbhD{Ji$lk#=)?)`P0-R@l;4YQM%gAFWRh#Y?Y-;x zXexfXXv1hH+m6@f>CY_UAV$FAWyuy4%gfJkEGC1IA7r!ux_jN$Lkf{X6(XgI0~jzu%7N^Qy^4Kj`Hqit zDN{rWMT!(DXpl$~xuK^(3QZ(8Boq`NfkcTb%E$k=yR-XlZ2=7p%1GmR`=5F9_2xa_ z_$KEjovV(CUB$Ui>&`v6Q%U$=N%+>I2X(Mp_x>D0Zgl_C`_ci<{sZw&74)0<53$C; z4T`H`=*7v|#>2^tL#*$J8ylR9gnj!G0>Pbv7G$UrZ4A=4*5Dd@DMP zukaCA{BpK&e=42XDd}d!H-(MvnrIM@n}B2=`7pB*>&|Rg6!wa=mw{?Oa;#1$m<08@jjGcNg_q-tV$ru8!3^qBSv*kgZ=| zZ+`TNeeFuz?j1eS>~RR;0k5dwug|9i|1I6eih5TC-~MN4$PSo7nN%y7#KPw4Ga+jX zAMCa7w>jXB?0*pBoBY_%B;f-#EF9`(y%%8~-g6C7V$Z&Gs7IGhbbstNd6Sz9-PZKz z*w`1{N~OX~5oUWPKa^w}VGRqzEgmhuBBhzi?vS?kwfKcvd^~&i(J@nz-u>sADdKTA zDH|dm?vS-C0fRqgXAt`#vk%*mYSO>hg~$Grj`)6-)1aLGD!wFa<=J$e6S)x2a>CPk z{)^|SI4-lby!bBD?F=|0IUy$24l(^la=$}NBUlWzbYU*ulVwWOTIqFocL8ywp9+cG zDQPaj$}R=vwC3HtHrHABPHOz~&LZ_AD*tz3SU58`4i{%DQ{}u3kr4a0#otHx9XVnD z+faokwX9P)p1r{6m>wr=v@y}3^Gqe4AmqE4o`dpB>tKteWztI82}c>*G%PB|-|zMR zRFsLSwOm8!UZ-b6SzrEM;}n4W$Q`mec+i%0Giy(#8{lRG9@;#n|4&mJo3V%u8S&p1 c(y1qe7B-Qjz$01?5b2A^-pY literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_badsegment.c.sisc b/lab/lab6.si4project/cache/parse/user_badsegment.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..0b797fcc9b655f52322b97dd309e14b6a2976455 GIT binary patch literal 1962 zcmeH{J8u(F5QPWZi5!SoOF;<;BwWx?k%BTp{DU|TBs%;;c;#SFL?Y9nRFo7HDQHs! z8X6kXQt$_oiULs~XlM}W%=zx_*y|M{G&G1K9nHNnb7yuQ_TseJx>@O}Xa%!Rx*xtN z1RhDky$^@8==R?2SCB>fkAI~D%>IISP6a&|Ux-=*J1v$(P_6f(?&V^I^ILJ@l-Wb+ z2Gh68=p4foPmg}g%Eh?4jhMi92Rk~y``hdr{Ilrj(h&VEwh*^E)?qt~A%kqp;c#wb zA@^L|Z*OdM?$(xcxV90u3f&v4(i7L~pV67P`f1pd=Ey-Jq`;YRqKGP~5r?s*Y)M~1=Tpia+6#w?WRjaQc zb@^bCz;(Z`HMcG8ZgcaB9<#J?+=`?a3W8@+`tyeV-RcLG@Q)``9`2UchgjqRo|6x+ cD_-Nvov`NQ!{(ARK8x}>{+jrB0v6Ko6Wd6?m;e9( literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_breakpoint.c.sisc b/lab/lab6.si4project/cache/parse/user_breakpoint.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..6eded8cef9e2df5b024fccd5dd2e5fe01627db64 GIT binary patch literal 1958 zcmeH{KW`I35XBeUK_PN-k%D9pAdEx-RVY$WAe1s)oDd{;oc|zvaxhjDM@CAKk`F*h zn*!3Lp`oVW3xtXSD4|BsnfJTBvDYUQXlM{4jc0decIMujk#C)KZp*pSO{pt5_gVLo z*M(Hy%c6%LPRjTW-@cs37TtgOR~TUS7p3O~=(+TTRBPboq$Me`SnEcetHpWFZ={to z&c*Vrmg}?lOh%ckefnNjisNcAY9h8f#PQkP-iB|;52dY3EVZ}9(z=TiAF%}jfrK?j zqor|#-b+cZwLNG*tlrUKc{^zqI(Ikar>@}#Bz0#pl}W`g-QI8Jcp%dw<9BB8xRLZY zg{;=@b))(f&nh&bv3$oz-%|F5!{#g<4DGL2RX7#^y2wH04#c zBBkghsTIXn4T{$Lzc0GS<8VCv&2IE88_$+vZRy92ZuD+$JpvI!w*}y17>~@<(sM^F zk28J#bUftOvNs`vd?#W)jpefU_}}TVSU2CdnDln@SPz8tM%Aw(D*@J6zF}p8l`8h_ ze+#RvAh>L>Sj2p%W6f<#tJB!IuE#7bI-$aY0g#aq4!@>5Z$s$pT6o`rkh(rpC zG%28`Nkzc}(1eP@N(l`Og3kQkIXm|HC;}Q9#7N`Wo$Jm&bG$TXwr*CuF1CW%vF=B2 z3n_n;gbzL)&7(Vb|GJEvvH$e1Ai(S|iZ7_4UGXci*1*n)i(+X1Ua!)etm?WiOx#Oz?`3UU>N}MQ`s}t3(Ay>LgTUPRS?5g*sX7YyysK>X`evult*z zTb1-KQF-_A`KB3*GNx%{C$FG>aqCIaAt_A&8_gpb4~1M;GaJKqYmkRBm$W%})Fvqv zNKg&(lB6d^SBsPG{66Ux!SQ%LXgu{R+0w7e#_}y`>Fek3R_KVK`vUM7#v?Jc^xPr$ z<4m1D9S{C1A;!^iiOtU%%Vq9P(_`^!VKtKe(|oUY)Yps9D=R^-rd2FeC%uxz-~P9H z^%aZ<%P=9={kqrOwzRv=!8HZ5wD1IqtQi`D&j$AJcLHR1SWf@(WIBo5b;?FA>OfwT f?`WuHYJ7f>uDScT#1Ef_0RQnN^YL$wg>?D}`R>4; literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_buggyhello2.c.sisc b/lab/lab6.si4project/cache/parse/user_buggyhello2.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..986fb0e76bd0e201323b26ddeeef304f76103860 GIT binary patch literal 2060 zcmeH{ziU%b6vvNg(iTlkp<)mPqjb>84h|iPvzuBgxY_(DCN!oJZ0X<-{0oY7aB%2S zP$w4`CkF>dmpVB}C&9%<3^+W$pZnfvZbNBZT*L$CoqNyEd+zt#H#2IsV3xluww&4A znAy&oT;NZV@b>4OBD(GOFDH;A_MiTh8vy$=;!|p9Q~X>EzQ@IRF|>Glx%A=u$OP-j zV`gRXhIH$rcgN8=B^hn){w!*gjIG<)L!MJ9LLHwakBwtOK=x^5@~z~e*yB-$9Fqeb zwv!klit{GP)SiXdsaB`nUTrK@=CrudZq;(lYxB}`=7PAG$!SS)$2x5fq$I&BC2AC! zOYQawS=s541v;UxXN<4XZ)^5(EmrGB$r}0%-@@vHYOBLO#_Qqya;bSS#R~x7o^;=z z{wPvywp$w~hAK$#LLc9ewt*ZkHu%EOQFz^v&#T~Rc1x1{-nC22zP@V42<}mQmLwUR zg8KQjl~#kKG{F7PJQC0;m;QD3Cfu~E9ptmRidQpPmWHy zMsN(zhM?$Y&uKxnhImW5ujLyhIs$Yb01g8@5(mQLe%Pt=r|{r^GmG)ta*55?g5?~0 zr~R;awQMw6okzpHzE)o^E~KN#NYLv?=`Nif^hy>#``_x-M=%B!Fd^3+-s{k`)SK1r zw1OF0cyJYIGc*MA(}X?zotnSi{}lk0`Y`g4eLm!*9^|$8C-n3`?ti;SFQG0r_hpUG P7u9a=rau1e;)DDKNaV;w literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_cat.c.sisc b/lab/lab6.si4project/cache/parse/user_cat.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..308d8547c3e50dfd9b986a22d6e6c6830bb93eef GIT binary patch literal 4012 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 T4b^`Y`mmQl???UlHyOM?`Gg!u literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_echosrv.c.sisc b/lab/lab6.si4project/cache/parse/user_echosrv.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..6fddd6b78c3a470ac4e1086d17438c75e59705d6 GIT binary patch literal 6137 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_evilhello.c.sisc b/lab/lab6.si4project/cache/parse/user_evilhello.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..b63341a11ce32ddb7145cc8579ca70e5c51d886b GIT binary patch literal 1966 zcmeH{KWmdw6vmH9Dp3gZwzvD@qOyYG9F+gPlNi+JG4x%b@j=bq=>H?ybBmd#361uK~CYrprQ z5b#MF^gr)au=PK_uA+*IGxz)X2WOHxHV4YSkmVD;}%J& zK(gsvUy}Bu*y>Tz$+?r0ZW4TCJQ~HT8`*f4b=DRJvMs!8)#!+!qY*B{cv#1jut(gF zHFf@UJoq>An3zHCiMUN;xtv+D-}nw`>eaHk#iaLayw~^Yt9(7{l_x>3Tl(mC-;R1E zi{Jfk_3BSB9xTH|-1hrk$6iaT(_FuzU>+?PQIR!6Lx9Nv51&=@gPm&l$CLRuao1nY l$VV;2>+=09DCgnV2jQu^k8Aw!S&>g?KlAZ#?wsr{KLM|##!~*pI= z*}WBa>~StTSWdr5kL-&j;%8W)&Cfql5^-$p9G#H$4(DLDx4ZrwBccvH(R)PP5+U)!V(PxM zmGWT4-RznxUSOkn?$Ky4!A(D_>&oic&N!<%sme$+r>thakEOA;*t3}v3;0-}xR11}1IB|_0 zMaaiad2TLVi@Qo95rtu`fT(N_9%g4t!Ax;tzPI;`o-=oQ?vY@UigJXGiZMbghWSQT zmEd~66ICmY&lK_2pNCEL{`-sZJPhlsAYK&K_bQ*p7*l59;|%klW!rf$InJ?T&g?V~ z$WwUkU<3bb)ob-#eSea@QGMT=wMyZGiP>bVnW1v}@r5{6W{_CBg{^wAqQ(5!typcw ziP0v96*BD4u_g+eFO5zogI&{y?$)_D*bylv*ggfjwo)rB?TRXl?pClg6}~ZLG{|8w zNye^Weiu(&-b&RM7Vl!>Uduu$7%ygfK>SATS8N|E#pR56bHb*c=hNI{1n~o6C0=XH z2=pE%XAt)xllT3LPijx!r!V}3SB47-e0rkc$}QJwKy>8AYG5`5)zRPjbXr>4D{OI~ z7=6b2E8<{k0!`uL%X~WC{q||%+X--%JWuYc_wWNBA@h~5TtFo0!Y)4z=~L z>T7ZCta$bQTz2q8p(`=*B1&j=>t{w#ax871HDoar!sjt8nQz+1J0w3rBHu~Qi1D|W zUiQ{??41;+DIGV8`fFR{@2^x#^UJ|(?zTQg<|~g{%XiclAK%WK)ta~&afjFl9hASZ z!&Z%=s8jaG{hAz|C>NviSeE85^E@3EqTZa9X7FFvRM~T zVJsRKliNCKl0Eyr?i@g@QTjYlWLEJR+E<=dg1k&=6^A^R$P1DrGF{HO8cd(vY;32M z69;WuUV402zGxW}2ign7qq?P^UYD}82!tYG!Zn*YHb&&JCLK0&e5S?t&?XxR5;C3Ud_LC1 zzkBCQRWXsbdulQ)E@S|i9`K<-hHQiEn0M7F`neSbMLw5e9t{U-W@yQ+@y1<5wUA` z?m~}qTW{JU-j;+nK5nJaZM@s;MRvIV^siKa-k%Z=DWT`$7h?LWG0;GG+8tl$8y@NC zWjD~~+-dR7gmkHy?${&})tH`be@|jJu$ipxl*t=7fxu+ejqw=?t~;5VAgNtV`EYtZN}wBzKY; zCH4&+Q+IA$@*T?xevc5+l|RgMv+NhDbp#YTCehF__K;P8HF2Xe*UfqpXMN{Z*ebFAY4aGVH8aIBg>`Og7lU z&_O9P-Y+A|`iR@lZIWYK|MiG7JS4#DKxL4;ZX{y5Okx~&_q{~*)JQ6o3m@kJWdi_D z5R(7LK+$Cqp#3+S|C{}wt2ieoa$T+Hj$ffkodsg2iM;dLPb6=fO&XUutcm22u*9r_ zNXRPKdLnHY;?XFUmYZ{3Q?A38{U|aLni^>ET1g8lI#_}yJ)pMnzN)AK}-4=kyFdm7vrRNTr zA7|?P?RfAnCNb76li2L6u{`B&)IJugmaIn5_|S>9AY8i>F;-Rrtb@{>cv#6|Z+|ST zwu1g(875@9KV$9KmP)O>HLhTm7H+j71_L0Nm4q#PQ^ViAx^4TaWxkIrzq g{u2tyee?4*c%s_a%$GAhduo2|BKEQG(EnHa1$@QB6aWAK literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_faultdie.c.sisc b/lab/lab6.si4project/cache/parse/user_faultdie.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..fdb1865a97bc342dc1b45747810da4e2a6075793 GIT binary patch literal 2851 zcmeH}KTK0m6vnUo5rc&qAR!_#9gKe_&}iad4C>;D1R{xp)V|U-fl6M0&4ker6GsOY z223y=bn4+IrsbSE8|C0bagiskK(_zT8{>LToTUcDQ3&TLOz^I=Fw$pJRl_pk$%nA+vJtzyqq3aMc(d} z#cZj}+(&Ffh&>*T5t~x_N7AZ%nfJvsr&Y#wBZM@Jssl0n{w=YF_}y|2ry_?TwxyV# z(iws!cch$QH)@tO8(($~BG$#Pr&hD5xQ4FPhoxXrp;ft5sFbAUYoD~K(0mxO z*GGLqZ}NPC`aP`$(n;Rw%UqgEf}>cJT0mXqK-7sb;T${tFx1&|SIQm(z$|Mv7Sgm| zm;Ff7RVjCy zA&rGr7Jh&<2|+BxQX5lW4oCr+uLC*o64YhWkEnh4(B>@}9oRO_7Ih|^yT-RSLR8M@%4x8 zq|d3?Ra@*eE}hT1N}J%|5yG_WVIa2Cw#4U6nb&zmyAkAULY4cjWUTc$(dk zB$vl>iP_KdW-Q7Oqn&lHEPrj|QPL$SMbHe2M=~A~xh^&vu2J)9xs|1$?X_NqprNml z(|Mp7Ne_xHE(V=FI_L(Ei{shWg742{<5`xiD-NVVr1#L1NY-h zo}W2cgrOg@>)kilHDlk<*`lR72v|_3+1&sSI~3>q9K^0ME&X d?djcmlr5@e}>6!h-++ literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/cache/parse/user_faultio.c.sisc b/lab/lab6.si4project/cache/parse/user_faultio.c.sisc similarity index 59% rename from lab/Untitled Project.si4project/cache/parse/user_faultio.c.sisc rename to lab/lab6.si4project/cache/parse/user_faultio.c.sisc index 6b2090fe1d8aba7d8cd0cf8a4a32cf2961c3c3c4..28d276c0a40c8beadb9eb844d173656ad93931bc 100644 GIT binary patch delta 497 zcmYjNu}%U(6nuBLr+ehcLBK$9F~nF%LINZd+MPyABLzka6G+g|Qemr|cF6ky8?Dfs zvEdV}41RzKwN_M;Sm-?9lu71x_w9Q#b9=pVnNF6Lh-ySC50Xh_KP6M9AMUigNy3T~ zuxbz$!0vX-8@f$;o8G!+)d zE{E3Ip2k%SjATm4GRVxb+@rf=5e2Aid^bH23@c+7rK!EF(`any40lwPeJ96{>Q1#c zA>X%id~TOjVr%R^_w5&BR|FQ#hWN>=xfz~?PQfb78V^lRl1j=bu>=*fo*q=%=sBLx z9nR;vs0|n9B%JvD&(%0^&I%|^A$Jk8*bAdaxInJkgCosv&dE(xbGWRy982qlWc#RV V-jDgYAr6zzy6BTFA3#JR`~sn1Q?~#B delta 644 zcmZuuJxfAi6n?$lo0?rp(iDo2s4!?KXlN;v90C^ugNDYw;8NU{mKKACno3@Rpf4kg zh6ek<>|+pGYHMh4^e0GDTRo4rTT&f(IPZHtpL5Qde>314Xb>VLgxUuPMTngXU#rq# z?iqUa?!hA)YCU*6YdIYrCm=7%#NmtDb3Qk8RRP7M{n zuUVT=i`FB2wXU5x{;&=28~VoL|7_!AP|r3EW|eD;E$$*_fNj_o-sWoy=T69!!b!}&8%@Kl7o#}f7!abXuR&$P^W hLU9uvv(KYm#MBbFA0|>0ymt#`e|oiI_@}U_EuT30YIy(v diff --git a/lab/lab6.si4project/cache/parse/user_faultnostack.c.sisc b/lab/lab6.si4project/cache/parse/user_faultnostack.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..40d57ce62fd01aff97a7b0f7f6ccd015c6adad2e GIT binary patch literal 2303 zcmeHIF>4e-6nvhTMJ6P)a`>hIn>l@Fiv=j1Q{*@ab_s5{q0Q4C81WJAnK+90d_LVrec6g%7 z@$4Rv6VSosI9U5owLbbOCX>O(ZxytXo!bCDoKv(WO4xn0mvk+yP2AzrhHnGff^xmq zw6<3G7&`>@#jk73R375~Q%N^U`|Z2+ix`|vlRKr(rQ7hcuAZ0n2$Z!so`m|~YZrzU zxNL(3G$tU~5sMwsKAq9@^4^EuuPbJw#q z?!0<#t}e#sMA(G6j54Mx-8svv50W+y?1LQu*sWOymOVs$i#FYU^wnM+!Ez0OC|CmV zH^S6Pv7O6hKIL~{dCAXhrig-8iq$f#<39$?q6EMmA$VH$I)SLKE`nciC!1%+`?7#W zP~~trj#!iv7C4&OIan=9U)?W?W`rvjV}35W%EdlCjjM6cnRoOoA=ZX|0cD#nqJW6l z;o4(L58OM_qj`*TpWV{K{KagI-j-U%bPQVNIal#p#2DQzYmKD4GLBV%_36y$yQ{(1 z^02asp8apXJD*sRNm1Xgv5pZNb;9N2SUjC+aTN{>0a4i=R9(IZqG#TW{#_tEE}A~# lXFcvm(S7;jw}6_hPfgqEnxmF^%&FtjzkhZ)`sV!B{Q+bo?w|kw literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_faultread.c.sisc b/lab/lab6.si4project/cache/parse/user_faultread.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..afc1bdca89821e46169a42016ae84164c0490ea1 GIT binary patch literal 1958 zcmeH{zfV(96vwY^A!-V30+A3LI=Hw91BndA1XvisVB%(LYsyQY5F&(;KY_u~l>{}A zx41a_2e9B|qH)xv3`Vx+_w(*M?d^+E7Z$@w&O7(~xaZvOIj5Z&c5cs#K?a#L>7uDhj=l9}? zYtGFqv@3hVPp)G#N;2wx{#jOtLp2{cA=@4N*i5&(;T_^bYDuY!?JmAFZ?$j3H;N;m zFlUmC^*q!bi#pBqo!0Z(10AN#E!-*-KHV<7@i~SZC9~+~=|M zH<@ikl3JF_ByKy;I>(~)Xt1*u55y7N#cmz8J&eVm!%t=bj(;oclfJwABaBtvQ!_Ds~y?zDqwp z$s~Ls2_Ida=dm4q`&2+?-GBO5I>78_#M1(_FFp`!4cvq{Cx*^mw2HT-Y=QGvae2hK z>QbxNF1#AUW)%0RdvTLjh(k3WIU(B}{MbylyWt(;LuyLd6x&^VN#4@HhHnx_Kw-`} zp6Yw3Jr#8tTf5CSm1jE4ZAGs$?dPkqlh^PAlDwlmmPp7jZ7*Eq&4!F)B0Vy`qq}eG zQHN95)kSr1Va9irHo=Fo-G01Ix>u`S<*8t3f7eyH6S9}0P{-Hy6R^%EfvL~B^v^xp ziX^oxmr2}qo_CH#>CW8rTrBMVCL$?LsF^orIXz9@* zi{nh4`;LeBTJKKSLEZ_OO=Fq$7XKX{i&gV=vr*@L8tbvJb{~1HtOQtVvYiE3$zpH+ zTUc!c!DWL*LT39j*3`B%+V!0$y3EqTgI0Jj0J3I;!~dW`FF*7RQAnBwRRRxrr~z3$ e9=-TdOY>tRS#gUoNgOf1_=o-JV=Vc&>_&Iq^umw; literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_faultregs.c.sisc b/lab/lab6.si4project/cache/parse/user_faultregs.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..cba2f77258ba9187aa197dfcad23c0db2f2b77e5 GIT binary patch literal 7457 zcmeI0O>7la6vwBfKqKw=9NB=Mtt?R$NFu`SPj5LbpJ z%W7P>FaeDS_?2#4xYGo8EV_ZXaN*MErcE?fjT!&HnS0($=QaH>vIro3> z`M7uPyrwmt*XemRd&OS0=Y6-;^X`6C9r8U%`1%ib>(O1meQN`9mG?jWS0X^}mGYWR za_Bwr2jY4${TeaU@j{^?lbJ#7?U(Y~)t)zXq|k8l+4cr>MuDR%_kORhlaj7IQ)z{? z!O1E~0Iim6Y;ue<4aGJA3~JCF$v4Hf0T6AJd|MznwZ9hMQ@c^ztu`h$Zzwadj%N>u zla!o02sV}<3C`PC9O@ky%jNy&)HpCu?5Xa5{1=+fyi)UY7(?1n z5MUJ%bJPsy3nS;%pktOK!qdXnIbE$JlfY2ZlhgIqrXK`YMg-2fvq#RicAaT$I}0)s zJ{&#t9z!~0bZO+wf$93>@+*)}wI4YaBA?Z0)r9AZ2^&&H6|RvsI-VzK_L_AHIbzLg zdh&f2hx5Slz!z_G@(D?X`eJlxSgFbs1S#0u*l=;Ea7G5C$QSQ$a*w3JqD#Z_%qR%r zr>u5$wr?~){IXg^uu%+po0w6%#2^~k*vz4HG1SRE%t@I4y59bL?;Bl|1Pdmq`y9qW zq!LHHp>v6=wPbZXl}?;Z8kN@SqsLp1zIZ_+L1+;}E&RA0LYu{FhX7tCn*l`-tS{>c z!}_tIT)xi_u{-IDPlyS|t77md=96M03+t3t9G5NFYqhhY!v*ZMr9xkJY+#f~urXeD z0PjeGaY)?dbO>AL_|wz%%x^^Ms=WPSH%#5wV755KrXUiw8@qMh{j_COcvN)?{xU4x z`*->qm~B#9ju{9ItKqz(pFBU*4JnR%gwL$dLg(Er*_^|W2}PVWG^ywZo!p$#p-)nB zJ#(Hl9kOQOgotz@ITV@5bZJmMDpL@cJ5gb+$`?y)TC@a*pdwXOA*Si($4qV7eBVH} zFv3#F4^_fak+xLR%@3Pi>PQJUlX-7aj%&9@c_;Qc@r2%U6JPe0^9~C0S>Dy0Gw)V` zJZB!8eWAQ#f7*>3qHc))6rWNyX2e&?ipVnFs5Y1;d1h^$aU7pzO0KP9cVD_#%)KO2`5C@_9&ys% z$~)9Y=O+&MiHbLpTb=B1vMSr|T8!Q;M(!13XF5NDIMtMX$FZJ>K&;LVu}C7#@Mb3u zOBy6PKLOeFO!~T$T4WE4-M{^ zi&tck6SpWe{)f?a<{Ov-0SAQTdIj@GMKV$rF{#VZn}0jkVHy(atUK2^oGtYYtH#5w zR8~2@+?#PO^9m1NV{;PbeIQ*ur-P_vwx|?Po2s(w*7rz zlE>Cway0@<-uV9kN06KvIAS_KQH`0y>1vE!!>O9=aG@7aIsj~Ol2SKNbbbPKLjToC zRi=s~NT8P(%nHl%sUn+QdOD@giJPSBz8X~||DNC(#A24KBH7R8m_-l?Sp?^=NGk?c z8pWZ}w687fKKFjHJK;5z;-tGd;43JMTTWlC1e1v+nULw`_qCk8aGf>W9Cm8(CdY9o zB4=<2e%G-LpV009=A}PoRgFxH%*^^}xzKa7U##6??U#yYxc5UY$K1<-c|UypR~Xb8 zbqHBK@-iLQe@)##cZHv^X2WeDX;V@?&ioSD>;ph_C=y})HoEq4ClUNV1U(*!(2g4h d^jajsUkfrfABaQE3V(B`iS~%S1K2A@{{Y%n0Yd-) literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_faultwrite.c.sisc b/lab/lab6.si4project/cache/parse/user_faultwrite.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..fc979607c66d06691e1c48852ac63fcf7e54b6c4 GIT binary patch literal 1958 zcmeH`Jx^3o5QdNYLDVeljhJi<6kAwe#f_mP#E-^uHJH%Bx=WUuz)FblJ1i(DEQ|>W zP}!ImD+_;s4Hm}2SZYOKpu4`$J$H7`-Vn60F-|f&=giERx$ir(@tAXq&Q)$nUCFs` zx*xwOW%@}L-T!i2!?*w8a2#89|K(p{fZ5-WUKgNU=?kgWz+IJAq)2qXS3ir&Es|n@5!X!S>NuiHs^JiT~D5r))pSgPhG7jM*%)@bg&r zqulpUmR?rNEMYs(Ime<5WRzLU^8G72Pm?Z10RbU^$1)zH6JOK0!5YQ4i&Pehwzqm6 zie|jZHl!4tl3G!G&7f#B_xqwdJPyb6Rik*@@Z(ujtV>aiZ1Yv84kCtb3&6)P9+{D) z=Z;t&XZrl6zP#J3&*WHH39xSR2R|BO zrHXy~-@Rpv}Mk literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_faultwritekernel.c.sisc b/lab/lab6.si4project/cache/parse/user_faultwritekernel.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..4802f63d2bef90096c7b696b6b63f068d5deb5cd GIT binary patch literal 1958 zcmeH_v2GJV5QZ1qi2|~5q#%VTE?iJRMWjRuMT8K%fI&f`!^TF=Nx?)BNu5VXL75_r zqu|O74RszMO(F^k1RXTEK)5^p@Ak%CUl5|9L5wsvyEC&h_y03X)6T6sSGXm0Ip;p< ze)cw(=(#M~{cu*q*FAYt!sgw7`BxZV_6yQ$0`yvXAk`YUE7F1#*}C5@zhBIkIKPw5 zUvloAd^ZbEX7HKBsSH2=C@RHqwHP%K+a2Qg>~3$vH{^%X(xoo7x5SdVvtu8zs{{fG zYsT^1C_?YKsMFlsX+5v5>2P~9dY0R``$&H38h$`hcPdqxL=4mH%AjaIY#fv6k@20{ zdC`bEoI+OnV(>U+6`IhRe0$yR345ojgCZRa?XOr>I1&3(F7)xe{RE8JBr)@Ota~r@ zJ(Q)F)iO)i&MVHbC?lC<*0Oy6!tTqcMNvRN2;i}d$LPdYb#Amq{R>oPiZ-^}O^PPG z%FatEx+t}x_^LtCt>x@|E*9P8aX6kmjp9zlFa5e=E$O~|wO+FfB8F}Yz|SxqnTe(6 zj#wUN`uyp5$gibuLI(Ly#C#e{#e4jBaxB)(_svJ0S6Qrw!Zowtv9c0itv+a%>qld( zRIz{mTUc!c!DWNRBIf%&*34j=8;$K7dd$+IV=6ot02vwK@J~g^@pVs#QnE7c5=5v& i55)TM(;ro<{^e=1;+A8UJaWD%XZY3USn5XU$E2sT`}OPmlVG2~f3hj*OcwnlS(2n{{u5cqbc{VL4k=tCjz`)<#4=KElxG(zGto{>2urN42UB_BUSSFSg&&NpXf-d4xL!t{vP(lCs|lcj9#$+esN#2L`NOdhr-pd zxBgajObVgb#UNxI$Yj_)=NzA9n>y<@slsH8wFhoouU973u;7oYjnhZPwh+1~tyi_2 zE56WOQU)Uh)L&j`GB-?Ok;C9vnnb`<#4FOxeNoP{Hf4&0kiC*v(9$GQmG_*ibY_NfwvRDBxBazb@gyYJR(NwYDm^JU6lEEE9bVZo;Nigpv>3Y zM4pxehv}rZnBC&@-9EFl;b;Hl=5(W6qtMCwL#VtwB#B$oDaUQMQg=`sTlRgSH*U_q zBMbIL51_;kpB{V3I;J}r_$)q?6}n8c=h^X$_gwu-#L0Q)?D_LW0t*W(QepjZyz?uX zkZ$!W{}t&>W^v{b)Lwr@w!bE|WU@ch!H&P8Zm^3wW6yMfozz!U3c%Wa{99ln4-g4i zG42>Flf#`|64=^Uy0eTW=c9BhUuQcDNOsr2+Ife4NF*{OOOn}dbj)Cn?=YqxwEmDk zvzIl-#kb0)e3D&!#Daf5r(V@&F~}@g$NF2v)P3>U8^R?1ti9`_i$};2F%l+$ri+4a zcXbyRpSw`a-|?>pyZ8zhtNDqgbYpq!ka!5$D~V=@EirC4iP+oOZ@5e{X6+qUF#&`X z;{Yyt$|{CRp=zvR0J2dY0~=Zjrne@Eq!uyp|1^Xh#Opx;p41uIzvi-zMDME%mxRcO;I-McT*dC!SN0;I_Mi5^pGB9 P?VE$PQREv>m_qU&LkJ&s literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_hello.c.sisc b/lab/lab6.si4project/cache/parse/user_hello.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..3779139e92fefc7136b4417676399c7ee25a1795 GIT binary patch literal 1954 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_icode.c.sisc b/lab/lab6.si4project/cache/parse/user_icode.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..87013d3c647d91881e2960d26a5dd23151d5965d GIT binary patch literal 2381 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_initsh.c.sisc b/lab/lab6.si4project/cache/parse/user_initsh.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..dd408421e0909ee5714a44703acb529fe9fbad7a GIT binary patch literal 2892 zcmeH}J7`m36vw~5Q*DjfN~;!(;8Go|;LxFnvx8c#v~GFT=0Z(N=)=)TL{JW_7Fdcg6p3)`rxg&Hm4}_0|8Uv>>&_Kj6GnamTyrZAn zTj5ZfbDzcA>YYi&5+SL@Q2p|=Pa=-1y<-!w+2I|Y-A&imLEeX=E?FVE3E11<@wO-N zn2e)V8>qkHdmtzm=9i1JnF$?E&IkGS(wR%*6Pv|+NN4P-PW5%I&A6&-a<$!M-JSHLLB;808ti<(h*8PDiu3r1JYO9@|H9?}Agdb~vQ zc{8Zh8aD!K3$SRKsBxxwW-;a^ELx!@rsg+UtYDFtu(Ug-eWGSD<|Ql=i@`RFHz;L~ z5R3E$x?gA(V_w1{G6vh4zM3m9P3aYleL@VeOC(0}#yjHm=i!}vDruCEA|U*p)lx7& zn>mFof2*qcE~QRH-zt3a&G?{A%RVlo@yD)OL5YX068Ym+nH=x~PlRjBqzdZjO12=Q=1aHRqaDUgL4DY%;lSDgUvjja;#U-cIJqz*v23_n-j&IsZM^ zriCt)a+T&}Zz|bRob0p=lI(r)UOj6hOOouuP9}R<_K9s!$beYFq+mRMkl)hgSmG0tNA&naQK^Op(IkveBnck1d84G>D?ey27I3Y{tp1u-ny^hkQ4EK18aL4X#fBK literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_ls.c.sisc b/lab/lab6.si4project/cache/parse/user_ls.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..47559d22af0f2db7f42ee09797fd37b6e7c1990c GIT binary patch literal 10217 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 Cy+@D$ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_lsfd.c.sisc b/lab/lab6.si4project/cache/parse/user_lsfd.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..3c1c8a8377b6ae88570b27309ffdeb78018fc818 GIT binary patch literal 3032 zcmeHIJ5N+W6dsp{i-H#c0UwDg8Y7Bv3kwP*mR2H)iWTewmYX0Fb|I7&7CvHPVL@R* zL*k>M(ZbrlfHBd+*hony7JB`@JNMf=cZmcG8skaMJu~OboH^e)XXa)bjTttkz87p# z#yr?#%<7v|$R8ld%b!=9sat;kvW2qF{HK2<1FZc)a0dWA13w3|zGwq9gr${{l6U!R zT?=QeyNu}q&)h0`3ymY`Xh|r7h}J%RZ^jbCsqAR2kmQqgo}*sUUU!e(5r5xdSOkxO zxj#Xzv5vNV31c#rAZV{zd49V;U6`6Hj%O}laBj-arzQukLC=^K_il{o7uTg4Glo^# zxSy}dMFVUWEzf9}E9Xk%^vYQinA8KN*F#`tT%=PMALY|7(jaI+1fAhB>-A2~pXLg; zUiVO&kEd8JKEy*oN`(AN(x&lWCxT9yMJ7YcuIHJWXDkkAjOl(- z+o3ht&CQkybV}58&=twUka9|0#&J5bsqHsAoxg%jRI|x=D=2x7=u|rB6ZeNUQ4qwl zu$2BynBn6Em~^bY&1r7#kluv4=4|YhLSFZGt6e ztN0x1Q{@VC%IAZ{*&j&-E8W&EH|MlG3P(iQ4d#i*z$%<{N1dGZKdyaA+OWeC_qF*G znTc{GS2^YPoh(;+2QFp_nSG$s*Kw=TXY8{w41E#2gxj~+Ip{G2K2`$H(01xsxeDKk zO^r@T(j2$xh}GDyd)tE`E*M93D7*X#Yvy*zTkA_cp;_n_y%;O^67No8W$!39-A76i zv2wgMRw1(OQD2zM&1$numjG-bdO~SqVZZQy(H4UxNUj%Hz5h@ph{3v~RBOFk2zT8H zOgbXg9@^mu4;vPecOG9G}$t9UxoyJ?)wB}E8+j|)=v5jpu`nPb@_gG?zuaAZPrO)3dE6)&)je3oH^gj znVC&0L(ZLdZeUF6`kecHk8{_*=}Yw|S@h~J*GphmfB0$`+wcCD|H=xm_m4>Ti_rVh z52b_B`=tX?WahbM`OCxo!?eHM;oOMo#690Ezjvsy6U-#ZSW(e)ApOh|OdjijFh?Qbt%d{vfskEW<|M`SJLV;P3AIE%;D zOZb{3$)E^zVh%1}tj6v8)E$=B=@*;j`Eg%fZz$Be3b7*W#<6yp*{)2lr{zRsvZ1_S zv{|Wy^~gMy%~s|0ktTXg$zW=h+!$xrJw;q1A`vJkvzDV_#fVm}a@m+;ts#^M zb|N2C8xNux_GG}i@@#QuSamP3KJfN@+-gLl?UPH5YJGHW;;Gui^w`*`3Z%nx5WS>x zFzL_1dG*z$YdQyupVrDGU?&zo6674nYZ`^^F97A ziH12vs<)k4y$y-`E(N+J?E9O|{Egv4VkxlX#9W)jXNydj=WcVN&cBxOo9HtUJDZWS z8njo|tcK+;?>)aS1Z9tTrdbH=ZYgsb76rH1txEA0V=25qaG<)>tPzwC0&q&Os09U! zf}pEYTL-;tugnv2X6dmkD?tqtZa0un-Z{ICR5`UDb*CF{{}8 zH4wI@Q$(`-*0Q8YZM$CsD;l$!GB2dwHzb6;(Aw-Z*rStpCi@45y#~LEYxGHnE55AJ zc5=0o2uG`LEMi!1u7yLh*=sN(vNy8pF()f*^;(B4Bq987kbOFv>#!{8qlSewJ);{U zybd?~tZlR|HRRh>#T5~IC)-*2>e5+ZYkKD)$U$LO5$txn1&0)Of2Pc7!hEJvYNL=x zPOJJph-SMk9p97xwV^o>H0bNq9}Mkk-i=RjO!2!bApGEt*cxy(I&POgYh{`?hGGlH zRP;~$mEZiB)JOgLuYz3CAD{YqC__{%>CXs0M*_^=v&js@9L(AinZ=i)L%3WKu)hIY ClJ(C3 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_pingpong.c.sisc b/lab/lab6.si4project/cache/parse/user_pingpong.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..6d5b906e00f96e8e0545e273c24c5e297622c869 GIT binary patch literal 2739 zcmeH{J5Ll*6vwYT1Qi?v3_jx;Ep}s!m{?GVLG2AHCUnB?!tO*|%tju@hFDozSXh`y zL!pVrMq{if`~Wl%3Qdfaegf^r|8Hi`?qx)=(8hR@Gw0s_z2|-I^lx`=#JQH7*flwK zcZYMUubN_BlZ2PvuXcbfy?fe;Y`gG*4rUb9Xz0V&4qb6T;JglJUS9L`d{G3awx?869>guqt^?^!&w?_;4c08~~QdK(QXQORQZMKla zPh1trt*UxWcbi%z zx!lpg{t+FKvz|ljLhm|uflI@D7yIrc6Q<*sqNp)PxNe&(hf@=o(}e3!?y{z|T>Bs3 zvxxS^QZV+s5Rek@0SOTglgrwl5cZJWV~rn6d8^6gK5aq=AH-);%lcYVdWgM|AHxrA z3M;qMxh|Y91>dsCE9JG2>!Pp~FIQr=IQ~|y_9DsA<{Swb_Ul|zkEc)`ucGWXwo6US z)_zHxk?fd~efgr%Smu?c;gn>pQQB@G3gXl6Ha6XW2lBsuGHC!mW@7qC1D-TuA{NrK zn6&)k!*)qFiEWQ~S4|#EVd;Ou#$jtzBp#UXxU_p~Y}L^=_QGBGaE-|h>eth|zg7;C z-KJl=>K|@~asD&h*WIutCSryxb|xaDxZIaQ9GBf8C;!f}y0-W!xq}YmzfC3)vgGuu Niho;R2l$A5wQRO literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_pingpongs.c.sisc b/lab/lab6.si4project/cache/parse/user_pingpongs.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..a9825aaf6d2bf7a2a80c6d6c748bccade8a9843a GIT binary patch literal 2844 zcmeH}J5N+m5XTQMSp-}XFybS6Ni4t^#l(U_jE~-+VnVqu*xk5-Sq0XPSXok7SXf|z zQ7Rh~OA0>#g%}epv@r1-SYH3Xd+)G^UE)Tgjd7CMIcLtCnfcGm+1>Gd&P_YlIVN`P z&b{k#Zu@n6#P22H^-tS<=+@u9>_>LE|Mag^fZiV#4++p?@l!GV-7oGGL#vmI>C~By ze%1%toEs9aNH^R&lR{?{CiLy&_dc08wsxk`ipYF&P8^#hyX)VHZ`42EXju?v#CBiH z*vCL!$Jqf6Jo5d9;o!~*@dwNE`O0EpHn^b0*-AOrUb;9XJvn!Y^&}$MCy8%gr|q5; zhK4M`xsei#Mn8w?am0XJ3tdVpJM1F;|jmXc6IM_q2x^+u78{(S^vYtTbQ7(+b zbulWdf=Q`%bmui>?2Y`~!q8?Y&Zl!}p-WV*nsa_wQx$0k4xPP5fw z9;YD2@=zj=N}5x2f#-DQZfmDY3>LA-lEFgSV3|(s*p(dtjhY5)xwLRihqSO)j1MHt zJ<~P%v{uSQ=<5;Cx0N&c)GxzM4S6us*Vmo!vYe>qQk)w#Ke4%BRGCiRn_H;f9vvGS znH-;%G0kV7iGN&-4-lBnk1_lp9$Xzq6o&QDA?xntQn@l4oWmDBtfQaxASRNpFJczN zPP*V(K21EHndOd09t0--(6NWijx}+time_5_H>>*HjgGbv$Y2Q7<(f>ho;V#=kGUS zy(CR=Mmqc0gc1oQl8c zI0Njaz^=ZigJoc&;gF=U)+lXv5ry>WR~qZ*6ey=!LrzoDZEn7;xQ zC2TmfjfM}L@JPZ|<}Kxz$yU-*w<5c;m)-B4HTfb(FhGWdnyo0$)R%mer`dqfw{cgC b>8-D^4K;?qYh?co%)h>5l3!KlSVPDkVDcA? literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_primes.c.sisc b/lab/lab6.si4project/cache/parse/user_primes.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..f5a7c052b7ee6047992145ee36a9a619df3f7978 GIT binary patch literal 3355 zcmeH}&u>go6vywhGfe5IL=(iXiC-ZIqJ%_5f-NP;uu?OV&P!@KO6w1>@dsF1bb zv9T2ki!N=5g#}hDBz6r8&-e4*J$-j3?X(*kagsCV-gEEyo*(zz`{uwh=Po*z-z|1M z&fQ(<+`E@O3BQ+wAANk+kM7Z%rzK>s`%nK$1vvZl;z1>JU;IGK`K}P>#n6k>qXYML z_m&tB=A7Fk{v=(aR4Jk}iakw#neA6AIi|M895PHnBz~-y%r>-141%AU#;?TJ#Ml!f zl*rYYjg*L^tZKiSg~&2= z;1nirj6}`l8ii+e^33SKx_#-HT@ci)_=nD{c<9^!XuA|I9vFkdndvELJ2Ooa3J)1S z?1vURvtfZ>(Z1*6-{QwUjbD-ZL>z^Ld}xd5szF5JM)zarfJpoe**4r^cfi zpd_+WOc9XQlBO#M2mSSC=M&liIx;a@1r!eeto3q(B+;9$9Dp{)7YCi<&A`_#F{MN< z6DLRFb<(%Bxv!j7Ii74raOs<2NSJ|04Ga-oCnB{nisNhsqTUQl!(O_kzIozbc&jvrh?i}g2qcq6c}BQ?pIjnz9FnHz;M#Ibox{q{!9 z+nu>iE7ywu2SWdn>uc#g9LsV|3NQWN%+>A*d0F=%A=7owwWFo0jgg7%G9L7tl_D)F zg@6xxHuJYS=*o1y?JtM6ER_sqsrwh&*A1uJ+Cq49dUB>Fkky8ReXYD_zE}BPCUs$r jnuU8>YK-FNbdUY5*p7iGJ~Gg@R5*QX<{rEuuS5I+h^{Qb literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_primespipe.c.sisc b/lab/lab6.si4project/cache/parse/user_primespipe.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..f1c004cb25fbf8a08294c4f8cdd16e52cfc242f6 GIT binary patch literal 4635 zcmeH}J!~9B6vyZ6b2x*KixML~BTkUQ0VO^VAVLU41SBg#kbr@Z%I4O0UdcIME^(lw zq@cKBX&d1B{kdd$7fJZSZaW+wbd(&QHpe$)n1b+j>@3;d zaF5>*{6rf6Af6GkA0Htvx92+M?)5YIPl%f&JNpyZ-x+tB3ro>l{Y4EX7vc|w=3jbO zdVC(sR!&NSyIbrNk?GMv6P;41R_vp$Qr_j~urb>0#i!=Fy~XBR`a<)RpL6brBq$Gw zpU!j$%h<%3Qq`=WPm&Qg;&w3fI3r7^X5-F1`l5!G-;1ix>e#Ax|Gdnd9&v6(x|R1j zHPEKor79K};|(=q9Og-%o-7A^V2Ab4dNq4rYmR8~x03&e|B}JuGV-PPsK#GpAzs(_ zJDSJ7*)l0rU-wKJA}UXzuSt?0Mf|HQbrRRb*-1m({ig= z$jnR40zA};-pGb(m8yhuNVQQ9X*%iqkeXUUNkXJ40a=Q1lFG50ZAeZv>r7Wf%rU4$ z!O7S=Y0i;MV2IZs(G?+c5Fm2{-pTGU?I28$jPKLf5YZJ8*=dv{`4^+%ZYu)wE-`!W z&m*~Kmgo)Vj9k7-Q)@x*kG2AK0RrRrKQb93aN61VDcbnsx~ZGCV{C&Z*`pBP}$ zY`PG5dA3x|lSoQ)Gw8Rq7JKyr2Z$AaE&Im~w`*6PNFRl1=_B!iK zT|xIk9;>alSj~6VhlH;oZ6;Q;!Tyhx>uY<~`g9fDv$;?Bg1xQsi4z$5)X*P=W$<~aGt_?=+hJT%?LXi%@g?_H?v&0{l zo}oRzCZ+1@ZvD{M4M=?|pIeM0KnaTXn}i^FY%pZHdT^O5-Qput0G;i%n}8BE0Q5oR z!(szPR}VlhMeBo}@r>0X0M$tRY{Z4z#kUt-?$WBDyIwdQFf`OH5Z9LmF~oI*+tj?L(3-)ZFQsN7}^;} zLZ(xCH`Qp1v2Isfm-eMijRX_xA?ZG`M@6yvf=a)Yv2ykBjkxUUAtBSl(ur(jmJ1&s84R+aZIw zoxQeaGZ-6LyOA|!JCvK=M^4E%WO=76{HVIG{NCiRs?R%f=)*N#F2^6~8ag18+i(AkT3G}9sISkU2AIQISv&gHMZ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_sendpage.c.sisc b/lab/lab6.si4project/cache/parse/user_sendpage.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..8ec25f8664caa8aa137fbcf64655d293ee341f99 GIT binary patch literal 2805 zcmeH|!An$86vkgVme%MD8buj3P}H~x$8aH$K{$dzg_%W*Ky4;xhD>Er+xADa42r;o zuG|!~bJ3!u8^cA5pcb`jR#^nE@Au}Nc~={ff;Q0yhjZUK=bn4cx!;}1?Do7-&x>_Q zyavx(YVo}F=M90pkwzBZuE()0zFKNUH+ui^uS|fuKP1_&h8{^COGF88uOub`PX`K# z=HA9u)G-X+aB#wFY9?g>L{DzDW` zNt-qoC9fsp`X-m<33M{D;cJt)aL@4lj+%?T9i{2q)O`L<>YNtcQ>EJt#q(EXr+#{h zhx)8Exm}w+>%#YKlGQq6_vgwzB;`PcKvHy{grsI8xeI)TdP&dRke9QX!>B-q(_oE| z+=bl?Hs9Ct4?%G)ql1@+$1Y^EBe(QLIpz;^n3;9G%)wpA2Yf%G3id=T^Vq=9#Y@={ z*{E!v1e}!6?hy&&F<7;kbR`~5mHbT_=Yb%M{7v)sCQ8#(>@qnc)~36wOn#yC-zBSJ z_r}J}W=>0{Dsie{9~vfeqRSY42EcD1-n!k1$=4OTEV;&k0#5`o@M+zfmZ@gz^*kPF z4Q@?8kzrw`lsDQkK8AqiDR7Y4QW00Y%<+mlX*C@UnC4;=k{2H&^gSFfS*L2+hGPOe294e}h8<|-Mj z*^W6mS2`>4C<|BKuE;8|x0fbUY31#ooGIT;cXjpmXDGpC*P590F=W@Me0j+`*=C>R z5*!U@jlYxzyQJ!}%i{2Jj+M3(yNG9;>>;^bJQ|oyY~pFvu$4`AS~$+q^xXDdht=0+ zPxU3iXnOseEhJW#qh86vzQ5V4y&U>7KB9rye%%nQF86X!fCO z<4>YyX_8B`X6>S6+5!gR!bWZJ?nSt9C-lMlM{Ey%@|m2{8nKpEbV+O9sw9*7cVH>p zJ@ss^|Bu(yPhY@#WR7c^ao>kr@Xui}%-D^YRgCwk8e>13%)r1~2mD9@y?O5kAL0q= literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_sh.c.sisc b/lab/lab6.si4project/cache/parse/user_sh.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..5f2963abd2881fba63fcb507dbec366eee6bb825 GIT binary patch literal 19070 zcmeI4YiwM{b;noKi;}32wk4Z>HESy|DMg8vvSQPQDNBwm)3G8_mR!*}TbdcDp(*R776hixOEp`UBxtg$+VJuMz?mKQY``-*_2z<|AVM z_3BI}_rP#t=iQea4?a-zF!wLNXjtbzyAJ(PYB|{G=?YOMTQ1pLs2@^B2KDK$gjZ?e zZJP9VUf|RE{5Qf0eSTZ~&kCC-btd*3t603on$E^+Mvo61J2{xo z-lN8jW260bBfAf21ZH?s66$rps~2i}%?rh1+AFg&Ywmk=-=T*de?(0_f`kCdvSG6E z6oGl6SWJ5vzj`p=e{%RywUA~JgKiNbl{S57F=r-!r@ua2z+w@15t{FRdeBjx^BWj}X2d=|YZ!WxQ=BDe3x$FJRBpU0FbUt+WfkWMU zKk)!7!SvVt07t!O+SNp-8Bk><4n6j%`?~fY`gP4^KfU4QzAK8?9~J)4^B{N$y;!IxMhHwu5|d2lRVGrKM|m=?N8iWNSk;Fpqu9Nr*Y z?W42a3&yN9o~LFBX77vzv*t-S5xn~3c;9dyspM*dm+(n1^t6gzn zL>QYU9XqW)HYD$$uv@X2{mwum{!q1`%tH#X5vdv*#u?anJ})LV2Q`j42)Ffv0>)EJ zZb~@G-}0EGnSq_F21feEVrP5h*x6i?h~{iY$=Mq?%VZo`E@v&YPylCXU$L`f8))m0 zlVc-I$tB|lDihbJ;?0Pco9->`I;sb?8>K{+%m`OiA02GAhA28%9AKfyzN>++DhjV^ z>;chV3hx*1fL8eDGW4KQ^n1d*4B04rSNuOx|Fr0|`Uw&h1L$PDC_%*rz-ZXsRn9K+ zr&zSrNqkuK+=HML6PyY!A5I;X`!u)nJm$DLY5bbsYo=Qye=yM>MB8n znSEo!&tWFkBfzj(Z4(NwwopbUfN?7(SY6F<(r)l{8$AIpmdMsHsJOvkX0HF_5dM=| zn1=!==PiPb7Yj~}Bdww^N$>#vvzQ3rB!rT;Ce)pdH5WBi;rw?Ji2;%bIFy6hB*Kf$ zWTrty%^t-@97WPqo?b0#65+)XQ&(|3vT+QF>`Ek(LQv2o!iy#9a|tRW7P{1D>qpd) zMp-q9@M=p;bAZ*T8**1bLV>7X^R!jeM8b-osO=;!P zD59;c8)4a2B1;S^PQkum-B1pzo9KQ}h@?*o@#?71c%mh#mvt3ZR|zVv*w4$>e6|g1 z$)L~rkw(#B?bup(E%M_ltko5JbmEA5H7oNuA(Flz#9HEPywKYA7y9ed1tqJ}Pi}cf zhqCObj*i?>i6ALo5+eHdg(li~WunLW>&usy^iBZLS0|#Mdm{Ig{=224Wk(BDhCyEu zqT-AY6%+{Lg({kVv%fxE?20NQ748cdLF9_?vJk;=Ac+iLyFvulA9(p5($Up|FrHT?QP|ElJh#I zXI@P%V0;ThI|+bQOML;e6VIqrmQKN4@Am>WBWHX3ZS!df{<^SN&hA-v+1>bYmVHTB zm(tlG$tMt5A&dKhkWf7(%nIR!A#Q)MzdpPd6^nYrbe?{M5Vzp07|&o=b?7`fOP96o0$2) zLbtNN)R=p5QSiDZy(9(yD!fC;ceA4UJ|lx~7CxpiKN0U4jk`wljQV~oKD!$GZWSU5 zN?M0JrEHD+db-E4yMCZ=Jb$$1`4&Qez%4>LZWFS2pz*@cocX=DG_z9@V183lkyPyb zV-PH-W1A2~P_x{4vE_JWP*IN(%kOC!&zr7LG$T!zriRL7aasHdi#Z_;ZkL4j2AAj-gcrJe5LlaTuj^2t%Xp!#Ghc|G+m*a^w$}R~v=M|2LLP?^1gHhUcySQQ zornY{L3qLKVeW%mTv#1WR9hY6g%)4_-8e<1)&uj|!>wZ-9HOm-ty}NOJt-!KEX9HW zy+Tu9yimaf)o=3HpjhOVmH_h`69q#j#-3?K{$cSrhx&x{j|h>Ezs3vYpE(uFPuGe0 z?rNdy)WYGQjT;P!8DmYDZ}@qTHrE(%*c0xlO0`4L6>D zEwu^TNR;KWDJT7xKUQwj9`Sm=p4voWk$%f%Q?K-o>?*e@i%qYjHj!bZ!*bblUXL}; zL^gHHrrBVf*duJl<+ACb^iTSyk5cP$ya}6vbz*~I*K*l3qjjo})@fF}+&9zMaU@WL zmdmDI=|AniBuK4Wr-Ih0@Qu_aatXVZ%ceQ$zYxW4O1!DFsZFdA@mngJsB8FaHM2nM z{5QuZ#HVKNc~D=brc;}!O`Hv8lbWs$LPMao8ldM&Y9=Q-bNYqYmKRckXdAEWP@9au zOyN}m=v}YFxZs{1MZ zrRJMHU9p#{af(WEU1}2#7N}&)Wz&@OzY|sbY4IkTQk!@r#O-gnY|1Einxl$;k$48{ zge{bUrLu__gr#P#Q@1}KyC#*&Gur1<;$3*{3Af+)A)PsFe@;kpoPAjB#*177<}9}2 z|6Bs~%39`YqFw&jct!L7Q+QaoU;J)S^u8ekjE3!9$+EeA{)9Hx`hoq28p}S0vUkv9 zylk?S#L>Fk5x+~t$OQ0p#dw$g(qi@vp4tx~#>)m0yY=!zDg%qX`Q<+KYr-E9@m#!d#3Xv4Mgrg3r()^Lb-+8;|QfrU@6ZjwjsEm55gY{ z5&RP&f=OHBg@R{x#DX_#WC94@jKLFQ10!Rt{)M3J+s3zYjX~m1g=m=*qJ^b0UZ^Ff zH*DxPsP-$1`<-HU8{)!!Jfk{t)}ITk+Mw{1cyF#x>K*SJA$qM4li0^1WqX9rKxhJb za1dC`x7~vp#~jkt_7oH_p7v>thq!58pttHv7t?aA)qk}&XEP6};QK;~vrH!amdjb& z?oa?{X}@o0o8(we==Q7A$g*Z6)p9xeVd=s_C}8a?c9zLu8c)ly)6qklUPb%D z#?)CHB}L6yOG{*hKpKW{C21VmG$s9=(XoAAyvq%#O>4cP<+5p3`kU`4e?ZbL-q|22 zsBieZTsGwtyPl7i+tegptG}AEE3t*RER{|89L6rI%~OA8V$+`}X?`l4l}&Hn&{cY4 ziSew{Lf0E?Hn6qjv*llS-LP>`t}*+3O?_C-bIo~I_=@-(oZUj!fO%oAheK?CXV4esC~}@Unr#IsJs4%3ziJ z5@;L}b+zFhLgo5}kj01c#Di(ZQwe9SOHeK{{p5@s;Ic%~XEts_?M7$3SfYlMpu))G z14M_4b6Lov52%SWUMw>86u&Vo0a`1M7ut|X#b7>RgO(py{R{oOJyQoGud+F{OS#B0}67@V{ht;vq%a@KYp6u@!XSL`g4!^rO8`sAIZ zkH}`_Y_mV)a26-Y2y@m7pIBv(?#hI1scYlI;uTJevu%&`pNnoj-Qt~=O^oHc=>}2! zu;PupYVyM^$=t=-ggm>`+Kci=>9e*1z)mhfXhX$MY`h|h(P033am)u`Sl0cawqrrO znbl90zKG)_r;s^IJ5;2P4pNU~tXqCRF_Ce92K0o1CkXK4)k*8B85ne3O$3s0NK^k)0-JpLy zkRR(Cm>7L7?}92GGBZ$FSkh`7JkGlAaKSD1vh2mf1pEB zCV)Zwq>#MvxbrX{g*&|HhrDPK&3K{c8Hr9^CwlhvA@OysfAteHk!xcMI(<+33pP3j zg{M>^>isiK{wa-R1@o5b^tB5s8wfo{)hxBO{cD`QEMNk(Rw`R#UuWdkJKk4v0$&T9 zQGu__#owj$l_bYDD;7q@D)DOjYSBw#>z-%83In_6VI8!0B{z)`X}d{YUHqgT47cUo zD+&abv?fi=S)8v{`za#^=&_|{tbz5%^f591T)06tu;@5`o&K=MP;`epeWR!mICEM&0a9n1RF0FtRW?sNAP3|%feD-y1>>PXvB-g)00!24ZRNb;elEI*Wt1P8x1KEJ0?I9i*yQuYHS`Yki_Hbbv2y4D za?;=9ZCZ!(!Y1)9ytry3j*_2@$1W%~!N)F_!9S0uWzaPU9cE11BbMRq~wmg@nFA{)ksl@}QrS2j`^T*|QYMBhh6x zO_9*iW1$&U@{55@*oIxurLgHklDo+xIt*9`!~JT2mY`F5S8JqQ8*gdA)GS6iV{Lrc zt~NIuSFL{`p3S#$@n_>3wSJ>9f4`>2Q z`s4BT`B$ai) zp-lSY&(-EE#H%TTno7L|tnPA<&Z(xh|J#78sW#@L;&Y%gjxl@mmPYPy^3Pe`(`R5M zgFWyt6ZoHmxPM&thHw9er;s1{Hb3VAI$zz7uCy=lM*#0F*V4}dp2D*^{~qwnpTF=t z$me|fMNgUgl5bB1ys!B79|yc|`!*MDfVnTbRwi@SQ+OconS{8{S>OJ2z}x2A`#fzD zwKlKb1MqHjt>ho67|;4Ad>`YiJsI$FC(|S2+uD3G z$cC&!RzHZr=eIno(1f1IxBupQ!QMeq>(If_{)|k=w+!D#n%mreoKE}bc@5`c)It~Xtw9av#eMmotN+AbsT|+q1yxCGK@!N;?c85 zERQvP{&YO#FO_FP2Dv9#>Pf(Ui! hfmlB-eXLsT&vAs`a?FxP&YsBbef2q3{457bIR&_SyXF7@ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_spawnfaultio.c.sisc b/lab/lab6.si4project/cache/parse/user_spawnfaultio.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..d2e444037e42f5d908e76b7dcd254d2b620970d6 GIT binary patch literal 2044 zcmeH{ziU%b6vt1~L}N*Pg$hyH;SCOTvV%eg5uv(jDity|O(cC#V+po@Lq`Wk*A6;$ zap~;f*rg64*ii>TCvm!dKYi~cH$lwgB7Wg;&OP_s^ZUK!b7t#iffeuI6Cjt(0l-i}~ysN?S9$mM7rAe4IP_?$lv%(-~d0!8uTJ(N1Q_l9<3Nd z)_XQy>#=J6T-3^XiTd9z-F_Gkj8YS5NB^^3AbScfBY*QVD{%lXI0R;=!HmYU?)X+5qLJ=OzvJEFCo7cO^=!F z-fkx6PE{w+IYJMcU%nS*VjTSrpTKvAdvt#GxA7b7V>EPF7V&lnlLH-hr-v?w^9W(8 ze@pDCv|V3WZ9FdDkZ^e=eUxk6d>}o!dCn&!&jVor7@ekc(k2IoYp$c zI!<{EP51bWxI0Bxj+`#cj&{0sPmhR2JDnIKPW>!CwDjCLLKvJ#Y^=jM9(q?_>E+dI z=u5h5i1sz~52gDFJz|yMfJ_)Va@=Ij!0nKULqsrY=-4cX{Eg294)BAlLCbn&3neA%Ff^4QXA=hVS;#b)*`I_#y`Ic&y?(`P%qePtd!KbL_C$)Uv((mf85S I_>g(`6FPInSpWb4 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_spawninit.c.sisc b/lab/lab6.si4project/cache/parse/user_spawninit.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..acc4ba8032ea69b7de66ee7a199f4f43ceb1578d GIT binary patch literal 2044 zcmeH{ziSjx5XUE%J3Zy%9wZngDYmfCN(&VQ5kUnjbCC!sJ@4eQ5YKR!{2dmSwgI)e zCLmTR`~%X&N>B?ScBT@m=l7G{xqEkrv9S@qFwBoPZ)V=i>|Quxwq#a5C$f^+m7`|6 zTcrbfFAi;f*sWpPeD`V!K575)uXKRfpBByNLC-}mL|OwoE-H&adb^X{xjH$8{8ltO zVb;IZNg9>qDmF(b@a^OGnp_Ok&xi?pcR0u9y5EiO;2)zc<$;L1Bd8B;oShgs9IYdS z>A@+vr?YNrb-n#4y(Z!OYWA?Sa{Zp{)aErGlT2J5>-9<;oHyq8Yv_&;D)QHdCE#CK zf85NvbTjVi_RUUms_5#X!v7F`Qdg^AmaAmz&+XT!02xkK;lJuoMRo^yyvoD6dSAY; zMLwoQ$(~Q*;Dxb#(TrsoAg(6_-_O;8G_C>j7~L?m)sns-+gvwAw-&at4) zUZ+J+E<>{gpA+|>*wQFy{||z0+|?y=(M~7ENKn_sN0y#5M+ifW!X|p?@z8JZDP7J# zr6rxUMLQb$*^>(iJz|yMfJzuTa@=He;C{&DAtN|!==d~5e#d752gE_vpy$yYaSb7O zwqguf_v!dsceLPdF6D1!(Rgb^w!QlBtp{C_{cpb2mzrF@DsbSo-@ldWa^PQXtu%XQ zB=8wP7#%dhi6%mReNBUYSq$I(qvOal6p2L+;Q3g^ZN;*$tpvOKIL8m4XZ%e*=RUqH IK4k9w1TezF@Bjb+ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_spin.c.sisc b/lab/lab6.si4project/cache/parse/user_spin.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9fb16f3e48c463ca6c2e157a68c079e0fe8fd4c3 GIT binary patch literal 2401 zcmeH|KW`I35XG0+MgbhoN+Lxl0W3k`RB-_)AdHXziH-n4atFpvoD;!`EaJ~WK}j14 zsM3HC6-7RPn@|8n8YJkTqCq;|@9xH4%pa$rK|E~N0F_7>~M;P0cM%^eA`78mP6-^STt zi4Q+#6ixJwh@A*q_0^5WQt`YNbF1Oqk(CQKWT&=V>UmU}yy}zF=hB05>XyFbdqypy zD5v`9Xi{fx<9;n{k#f`Yh)-!$sU*=Ywj_E!=?*=+DVK( zvT4q2t!bKJZ}4mQshwf#!64VJxIQ_XaAl(5x+~jgq0g0?iTj&e?aT4!kr@PL`!m;p zA+N90+64O#y9Kdx30ck{`&qV9&?gH?2pd^@D4Bi$4e_zphdrsF$y6+cJmfRcJUhfF zt*geub~gSIk@;6J*TJ*j*$4ghw5L952gZRuJ=DKm_o>dO8FS!_!S{Wfe7YYJA7a3< PM8$vkzJY#^V!;19UQ+F) literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_stresssched.c.sisc b/lab/lab6.si4project/cache/parse/user_stresssched.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..82b8fb35fa984a62eec6540ff49c7a3fccad722c GIT binary patch literal 3048 zcmeH}O-NKx6vwaMV`vj8WcD>gAB2l6qRcde3R;!+0c`?x9QB2bGO4s|S@f}L8MFwY zg2GMEqGgMgZHg8xgjOvATQ$4a|99Uz&U;g)n>NvbGv~f@&;6gzd$qgKxl7K~9}v4* z=WcIs?!&8E&mSe>h0h;a&@H@u){3li|LI?;0KMNTZdXDN#gD}FS7V@oh^YJYME2A1 zx>mk6*ErWHUXsq$UCG2IUW74~zx-&?C^ybJEZN!Q8Gj)D8H?o`$zd_) zQ?#j~V{c<>GaXA9wyo^qzbz={r{=~AxjropP6ea2lP6Udu7hV1XHAX3W{s{vyc&gJ z)5;b;n)1_g#o0pXg4TRn9rkJs=5BEk4$pFf;WS>5zw4OPp1?K25BAVn9lI=>=i;Yw!Wf*F9Fp_0 z_z6LXhQutb8YbDZ^^vIXs5~VnBn-_euIl`BFa{`zQBHzKG7dO0UCybBG2OgZUE(Ux zkLJ3cyIc#~##l(yv6SEx(6^!Vvcn5nN3G zq9Jh%6q<$|Yjo9!)XL}=#A_r@Oz}xC4bgPfh&Cdi{?eiwg+lS7HY_y2;GCE+?YkPZ z(iA`mDI%OT&Ww}_#aTv4<>M1mn4&zyEOeRecbb zUP=$PL()dfbV=aX@%@sSr!ds>6cfOk#-~d{?miLp9nw!%7ttm!@G=+pSagATipIoC zrzWzCFY{R(^DJ232#gCX;V&gJNoI!*Kh~YKE-?0k@fl{y?h+C*oi6D1NE7|0FQ|Eo z{MH4<+3H+plvl^mC|5d2uJ5HAeo@I46wdw4T}1?n_DFVF$qpnM%Mb!($y%eNN)ZJ~l9k%*lZ{B)u};RcsA21g|5~s|Ov8%l z{o_glh=oD=TQThsc6-d0hdzDN$rIimGqr&R6=S)f><2&6q<+mTrCjA>y|(MMOSvI- za)T^K25M39zx$TwW%Bv4NA5`*r{1`)k&QXu$AOr5eW0C@aC(LOQ^%+MmxJynB=;y^ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testbss.c.sisc b/lab/lab6.si4project/cache/parse/user_testbss.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..9ac952bde3d88a440665118bbfcb0af48d08dda5 GIT binary patch literal 2299 zcmeH{ziU%b6vuCyL{poZV1?q3A)t$kRp=-}Q9&2AvDNC7UnP0aMhUjy|KQ-@(80A( zom^a{gM*_>T@-N?L1=K$PS@|}y?64SmKYZo@e3#C-t+68d+zt;%s%Jloy(q*xKZc6 z?ssnc#b_vRrIC&I+XZYJubxhzGwwhBl?m|nMW_VtP5KkG9X>l%2ZF1B5z0gEg1e=Q9xA zkM(_=F*aMN%-+6n<<6xA?XZwd4Enf)uFYnXl4%dTPXGPc0`Ds4~u!KJv!Sf zkV6GDgkX^A2hcV|`kI!5V;CVA;WOe07Um?JZG$n5&(<@}!Sg=S{dmvm5yHOL?gmYY z(m11I0BBgnL1rr__1(SGy@FhQ>?vC7wi*=W1PV;W`k1s8#a2!gElm$Ex^`7bTwq+n zNYOBfqGprbCLwF&3m5wFp2!F^HxjT9JOwhUi>s_75+>Qwj(YR|# zw$1V6uHlqq|C@I;Qm3f`J%HJMe^DH}BpZKhBug7$=#VeY3OQo$v0<-oPQ}CY@{Pm%5a5 zV@I6ZdYkh4Q5IeQyp_hc{{D3bw%Prcf8_+Y`xDY`5qc_pCT*3f3{()IEZr<*=BJuF z`1;V~T)*n%x_qyuXWOF?Z-Pj6zWz+JmQS145-%z@FM{#=SS{Jw-t%;5`F=D{B|4p+7L}=3phwh9*JK>e^hN|MbL`7iWqkYA9K5o0QhQu-sng z>=zZ{3h%}9MY)VrWEsrIO#3r@KQ8>NGBpX94|vV z3A1v=z9Hx6s#ucw1wq~Mt~@N}0j1VBN)7}|{j!nyvd+alw|1$L)>JIJKxfK@9H6iU zpeM2?WepTxHUYXc(in7QT#Fz;Dp1F^NdYyV{Bfpo`#CHU1n>-nBeu996LIJX^IF~Jtjt+p8Y^nh@(5K0; zsn|0weJ5Xg=Xi$wXjMy&d}t~e;aA^qz7dOK%sxi3QBx7mTgMu!XS2-NSdA0D5jZ8u z9Cp2R#nR(?u3yA!Bl^Fhk>Q$^Z?&h&m7H1kH@Vs)0WWJkEMmTcxz;@rx%rtgi<1|- zEn>aV#WDxkHTmxMR>?vV!ba9EO0F8=K$7yi7kf^Nx+AqU;DBd(L1WY?b&eG>OP6ck z8cip~LX*Bm<3CxmFDALe3o(AYQO|D_GMnFWw7ciW98Hmoc|s9>4`Wz^z~6-CMox#u Hsma|hg^vsn literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testfile.c.sisc b/lab/lab6.si4project/cache/parse/user_testfile.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..ea5d845e7c2769953d5ed30f7c0b61fa090246d2 GIT binary patch literal 4959 zcmeI0%WK?K6vuBSX^lE{Mq?k^LI-q_)`h`VL|aAegNo9oZQ2IhB$-zRlbJCuDvB0d zxN+gag$qGJ3auanMCc#j!etg#y6B?fMqD+5*p)xOpZoirnfsfGlT^t@J#aX`d+xp8 zdtUe4VMew(H|E^HKC#O<_d(vd>)&KN{~!rpzIi>1?((-+w;}u8|MYK(0J(oe{IDGQ zSp2DYi}*hAfEb!MTFqU#(7%oEYkkfQsZN%URCDf$@*p~+D4{z){hp;p7}}h+cv87} z5{y5`YRUG7dy=JITuTNe$ti8liYN39PKs2&rb9lEnoQdiMMJGw>=>%mi!+O*a^bK( zo}8&oWu{*kl^)ySY$6Xyf^9nD^;Z-{=ArZVrhj(63MN{Mua}A4d=pfQlRiC}PYk*+ zkDq_(jP&ef-#y}o#OxtyjEyW0qa&-h)1oNCi+lYT+qPV6G|P2R(Ni7JyJAq^5N{V7 z8EXFEs?WUXLEY_M_d}1mI6GOZch=W!)zXcmzP=Lt`@pH@h99fuIBn#q)hxcj?4`aW zQuW0c3ggCFi~f4`bwLOH;Srp>Dye(n$P?#VOifDF(j9CE2X)G(t~}N13#YXP_bf-q z9b(QgT_G+3Y2^Deop~Y(G5H#5PA*IX$^d{pi7ZGOC_0T>mvu#;%o#;d2k300R6eI! z)YJs|M*_q~(>)!4O6}*6P!zS!^TrKU=4;L3)MCYoiWUP9l4}aE>6E(mZs|jcOEuQ` zqN>c7>r1xVJeg2YT^Wm;7Os@UFCJ4-?KqVq0bBqi>(?tP)){fmMvIC)#o4@^{ds5GS&a7KlI(SZ{)w}8W5Pl-&rH|b zSqp7!Yrk#`SDpH^#5R@KeEyE_00k*ORQ5)tJMmmx)N~8G`IcDD4uh(pC;y}+-vHX_ z>vanX>Pzvj3f5^Q<2UgMeIFOV*WxcU=IQ$^38obRen2L}Z^b24r|j)2f~7{SWUXYo zP`EcpyGN!g1ovn~fzgr{b%9RKRg2b20N5!;?iN#VrYi)X%>(O$E}hh7(gz?ZLDEbC zHJ$XS8_%3k6tzdBsBaELLt+~!^b$iv*NGSwb{Ap=L~H`-{zMWkLqvC1h_zGNLE-HZ zgM_sFn64Ai3Q$W`0BwuWXDX$$61b*cY~D!ZgJO&{U2jNSj0woCl~S?Md<#p7-lJl! z1TrTE)^we)b{(~(+tT?3IsrvV?Qka2oHt!3B*8N3dOP)S)HF#Nro|jTBPK+`Y3cR` zWt4w5ZtJ3M^y=qqQA9U0qyVBh|iUwbu^$#581dsEC| zZ;Lq$15CHq^^pxv%lhfl4lXgdi^B*s(Xyc4Qa>9$A3t7W(wD@`>hXg~Kj(Lr6$eIc zc0advVp2!?Zh+0|TWwD@4*P6Z7S+!?ef?a%j-O3G)GPA!V&JR4^upduU+H|%&BZ z>-Y|Bx}@{JJD0YpAl>+`woTZ=_1r9*9v3#RA8dZ;(Fmb^rFC3eNorXM4xX literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testkbd.c.sisc b/lab/lab6.si4project/cache/parse/user_testkbd.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..d61dfc991e4cef6008caa8b2e0ebb50e73d58d65 GIT binary patch literal 2557 zcmeH|zfV(96vr?9Wz}LyK&$ar2M1lGCJZDPW1_RDpy^0yrEP+xX)J#Oql2RZiDA&) z#o0kO7MvW2ad0tdClZ(E_w(L6ea|OB99WDeIX(BDd+zuAyswp;v-lxj)wD$9g|zu)uOJi0)=t5MY<4(DXM4LFzk_|88rm$0xeG(SoQQPnPx&X& zPaMDWcx<^N=|6i1DqknEDgsbXSSYkj-eS*JTA|R>V$n}eXZR9lms}k@uF2YGF{oZ z(MU~9C(D`ut@WC9Kz$FAr4Wcd87R7P4D|VpKZCB{3xOzQR*ZxJHJ$XMNTv4AkdWtL z84$sZwLvr_u7Pr#lW>hLiAb%Cenadn=m->gKwU0~xdE!_F2`c*ULO{$buv+m+ztqR zZe^`ko|_{eYYvCIn$nGhU`iLYZ-~3nRnN{BamVaipa3$Z68lc6*>R2^3x~-u;%q;< zFC}+uP)uTL>zSYmsC)u5TAR1PZ@S%imdv%xcP`A8m*MBC7kTwelq)FQJIY+`^^up& zG!io1!MS1;5b+Ra0yYC>Ts^h=U|V(O^VljER^yi7z0Glu~Gu76_5*=tppHbYNl7 ziN(Y?G57&kFbqbG(L^^oF+To(&(qW176Ur47*BF~?>+aN^FMF5&rq9l-E!oqysg6h^`VA_y`gJ!h701!;@d?@NaF5K+X6xIa@6)Uf(_(DvC|Q$n zcdYKRK8_&hs@{U#m0!pdR%WxQ>)Ko>DcMh$P*;?A{lSiR)ca-0aPQdCuswXUi< zhUB*a9Esl{ma(g@S-EA_{|>S(&@5Bet~BR`&QbndEgo{jvuEni1i%X#tN|5}`5X=-Ii^ zV=XSCZ17kbuUH@u)0!JTm^M_#?&{GClU-ScV6UKl=39Y-}P~A^fn{n4jisp?9c88g(J!!D)p|C+AHGsUHIdd`FWbIaLms(Ra;mQB-s87_c3Ep*xae31Z-pQU?fQYHl?Qh2D?pndon7 z?$5zZ_54ReOiPlTOVnZ_7Vr)$rnB-*AFLPJh6JW@ulBHw_7}yt`falAPuRQGb`P1U zUHE5zZ}-)e7>EtBIGLAK_0IjwB#@g9j(nYGigWXOq!SK=?#aW5d4ueMYxcsKM|PYa N5i({IIuR|Q>=&WXM&JMd literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testpipe.c.sisc b/lab/lab6.si4project/cache/parse/user_testpipe.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..f3ea0568ab555d3e460c314c39e16122e18dcd95 GIT binary patch literal 3651 zcmeH|O>9h26vuCCO3N_y5si?Th4|Ri5NQ#m304w{qRW0v&Ag;)f?{cDX<=a@(M=Pq zx{+8~SXg$$%0h(1va=8$o9F*~@16JVOlc7|;w0yt`_8@Re?H%RrRB~II+xoecCF4` z$UFD)No&NfB;lFYFZ1YTo%bd0G-|)3s^+BML*y zEXniI)yf$iMr$=VqBVGj#6FhQs`EfS@8=nYZ7%Qj>U50LHg#zvn50vFw~Fda`%v7i zLAk4mXQjK>{W(G__15!5ffpp*BE#=hewFk__~1OWRJYFQ%wx`pXXNOt(l{acRs2Qk znItW%s8z(I%+f0D)4=g=~C1@n$fKSsEol8e|c3)kWUPcQ* zN3K-H0L23UU5VT*X`twe9_T=6Y0&Eo840Wq(*mSLYC1VgBh_}~5D6_9veBuFC#5Gy zj7)+}pr&iaq*_K{$jO*UxFsM;5U@68Fbpw4MWkedf?`3GwV+ItWQY;>3UaO3Fwr$f znY9|srYLLY6O?rc%2rA8qMn{Ejq*-UJ>Q7fp_4HYeMfMys6hMrO0=Fm5ZT!-1{o;R zNv6ni`oJ2Ivp4K>;}ciM376q4iAm*v7*Jog)wQIc^O~o|qW&mc&_FS#{rdK+=aTzC z`m%UVy8Y`;74XO{gM_f-K49&CXS_ppjx~NPqNVNw&ND{XFqHT)sb#v>x4wmUBR_@( zV-!qYYtHq}(MsV(k}G!*xeiG8p)1Rkn9c5Q<;uudu6A1>A@XLuc&^sHxW>7$>c|z0 z{l*R}vCm1e?2)WfV{dk5$ud7yXp*%?anvV*_32j{>&Ns=F+IEx4;y&IG)+@hW-&3i z;M=hM>6om!R>C>?-=F*thmx3T((T-s#Y8OJaTb%7fBXX&6eh9p1Ls5P5mN{HV>r~aSs~52lL9Un&itKJs8$pwCf|lj;qA_jr<#Pu}!%A zwPAc3;m^2VYuNf!Q;Y)-J~sS|wQXd_?EPou@7Vp0O>;bkVX}FD(RXS@?I5cob8?4^ z``uUpy`4etpSh(obMNCb=DZ(+g!pGcJ7N+2!}vo&#poIT*Z4I-2V)VPV@{#oSVX#S D6{Tvt literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testpiperace.c.sisc b/lab/lab6.si4project/cache/parse/user_testpiperace.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..890d04f63c511b82b183891ba7559f43a21d22a4 GIT binary patch literal 3682 zcmeH}&udgy6vuC75{-3qM*I^~GAQ9(b5e-`T+ zxDUkLVrb>kO!cel~u+!EuPqgtwC?_h2-pGOcJPIifX*q?9B-SwvG!&;o3^CwHQpIw!n+$`tMwFY-o zZ24-L?2k7#^wy*l1U>5CaHzn(=kD#MKaUArwyv&zF;ktbZHAt&Wj5KPu1fb}a=uEm z_L+?e8L&puReb-hL*dbA4+7A#+^MVAWcyUSt^jWo=tntT5`V2{j_#p!SH!=Nfar?Y z^5HpFvrN|KqcTGsKiPbPz^adO>|R>%JwO2>X|5fTBP5XN>dqxst@dfL-38EQXQl-x z&TwBWk4qXTx_TD$?9QM|m*qq(om9g6v<5VzOEQ%@+(SY^fMrA^*3|~lJom?I4$u%| zT@EqZ^eu>;ad~T|hp2QB^h07pjCDCgaxn^m^odou?R#XoR}2=f50a~Fn6d61n1#B1 zvcw#uW3kj2N)TgR4l!SMAtFG^n0zk#;(!Q@k8zio)rn5zv>Gnm}?Tf#X<^ zG>~#kSA;}wv;}h7qjFA_G)~0wq$H50D?+Auk!6}QtMYfgHz7%Snu8(ECzAMll+h_h zQu)gHjc}A`JER~;lLm9IKV9#Pj5Nq8`yu#V<7wzx^dTUoCw?K_z_GC^@fd`bEBP?8 zByuBwXUObW6UVCBX=Gtf56#CW1`+~?wZ;(bO(;KxELvn=_UG>uYps7dQ(e9h)ykNp z)?51M_4cK;f`Z>})@mb{ST@Lzkm=rCYhgOJW}BU2u?-b_FaE+$3YcQob+F6Tv{;G+ zYB!7hR`}%BFJ?5H7E>hG({$O3yn1e+JfYgzS;OjfAaG$^tdkcCMtoSHGm&t7u=?ZmU`EGHg~E#_iQQY^ C2Up|( literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testpiperace2.c.sisc b/lab/lab6.si4project/cache/parse/user_testpiperace2.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..45d591ecc9f5a924925e95eecb6dbba82699494a GIT binary patch literal 3434 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 literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testshell.c.sisc b/lab/lab6.si4project/cache/parse/user_testshell.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..4eff8ba6d827e3b14c23bc9f916cdae9be866919 GIT binary patch literal 5676 zcmeH~%Wu?G6vuyUr&6V5MyTN1i9rJ)DFY&6N|o}8i3zk&l#Q{a(`hHxX){>p(!ap4 zaDj=5Y?^?pZrrCxcl^C1yzt}gZgdOZ-0DGgXxRVs--rOYe@d*h2cL>R z7k7#u6BoqL;)%)P4G~zDaZaOI-(rDOP8>FE^5KFn2`wI4>Gm!E z8-|SVAXXhUSH|9Eb$F$ckl{LXIbamhhy~ys2~oa z7M&M^N+geqJ+;NYR9WWS<{+fzJSWNb+hV75hv ziqmJN;`oNrJ~5X$(0Ucem|<~xj?Jad+K1ekiN;g9g2Rvucw&6v%1phg>fw})w^vDy zUEKDEb6{Apoe^1!&T0~CVG6)V7*NwGBs629&nOJ>(-X$E zj~@V%C^84L{|HbiuJ#3Uyenc&mvXx6G=8WyJ>n3S?%qRV6IJEj)Qs~!^^czYzDkTgtmc})B= zYQg-VRwY3s0ZfVpX{Wa7@|aoQa|vp=oD)8#QBjQw5{IxEL zBiqjwSvY-r1HD-{BK3rA)Cl&OR5NbtpnsNcqwI}{yPjxu)EhkIGQQrCuTzn)cKaXG zi{R$g#8>7rZ!>+hOAR0Gsz5@fTiaL5p1*Pm<5OczCZ`iSL=q!s=_9cfIlE_P;w%-+ zmN94THkT?x1SCxsY8#(UM0yA1vTIiNAdHwt)NpV6}D1-hp zejM{@Iu%N9)a^vtu2r9jLmGwy$HgQE$*HTgV_i9Qbj|kVUq9xSuRgn5(Sl_!_MK`Q zCc1J8b4jmvsbXrm4RjA;CqW03ZI9$uF-&yj6z1aobue}Qfk_fiiYWvnM=O}7Q-6GH zOYQoQP>Y?H=4xl82kVp=Y$V@cnojx*rn+KkdHpn)ncgRzpm%*qovdO*Vf~Y2z&*F7(Fast)h_-zS91; zz7FX){^fl3?!6&h*RI4@a*DqX%U9cJcG2Q1?FaRBx90RloMheSjoiT;b1*(avpR}z zR^I=|ouVN&)Q#VupN+=cNW6{2axo_-kmZIGD^T2*ejevF5RYNu^q3`$34Zls+#K~E b6C3e3g^qgRPHG;)(92#p-C)!ni6r80((NL^ literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_testtime.c.sisc b/lab/lab6.si4project/cache/parse/user_testtime.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..7da2e4b085b2def3ee005c3861fbadbb31c08194 GIT binary patch literal 3364 zcmeH|zfTlV5XT=sP6-~~DX1|T-Kd2Y0x@7Th>hAR5{O!ufOjWsf(PN3{23G$78W)n zVns~+4=kw66{3v=i80YmDzz}{_p`gRw|62)VPl+RZr;v&^PM;IzVBW4F6V}vYd#}& zjn2K<Ew^Et+<*C3I>6Zx6aKx%@yjFfvv122+BgT}&2q?{ z$ksU)E}U16L9Hgg^+E6nF6Urkd&pnh$l92>AjqhXoK}R9nVBe+ra&cnKW@nhDN*~R z)w-kV%L8(BLczCmqt;em%c%gj9(^~?3VSQjtGV_w`8{CDY!d@HYSw~#F_RO$km zSa=~*!wbF<;{{{ZT0geq#Sg7rmJUmin^JOHyjkXK+~XW{PbAH`ARv1rtZ$y5^os); zSP^zHXby?2DV}r5WmxIj2n*G8C_!MUqfIo;lnP+t)%zPBANIHuO!KKJ38vgN@(X5p z@*$Y)XSbAnVm&FC=Gzfwsaz!1YSz$Ibn(9pMvuXezKhaz`A#3ak)uZt6l?w3>d;wZ zLLTeVc85+3wGw0fH_M!jb&tqC_XO*I?%g4==^mBk?VkzR$^%{pysQmIddjK zQ2kg_u{RQgyEVpu_gAs6Z@yJzg@?EsRgl`=v!Vr mt$Zxx@Y@<{O|KbIP408F#`>_2fLQFQqz@+cf8oUrUV@*p@Kibg literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/cache/parse/user_writemotd.c.sisc b/lab/lab6.si4project/cache/parse/user_writemotd.c.sisc new file mode 100644 index 0000000000000000000000000000000000000000..214a78d6019e15bfc2976308bd8d485ff323892a GIT binary patch literal 2797 zcmeH|zfV*_5XYC}P%sB~2}WWxc!kk3A&LowF=|ZI#KzyKlpJ>lT*QMM0*QZtC545B zMjPUfipIvu!ot!9V_`vzl@(N)SUkU<_uky?I|$g&7$=#xv$M0a-@_K4GB=>3I> z?4vvF-N^66eQnOoT%5>$X&dQ8XB5Qr;@i)ROdL~N!zW~Shjnx|x4r%z?0qV0GAd?o zf^tnAYdwj@B#t2HZLD&3Z?#sMnk!fGXC$1Ms*a^5&)t+BY=-%$Wc(fx8(xWx_2r@U z40EF(=urOp!zuP1bN7qYni?yS)iq`C)!itoJu;gWFG_d$^m>@Rnd9pj5Ll+jD(T)V zLsZL%a}4EnS-maWH{zE%T3 zx?095!2uNTjFgRoOqX}AalO_CTT|TPv{;`g;grpAUpyYrXim}PJ*SH&w|6>!U7I+o z!((9K)YCPPN*%5-LO}qWh(d7n@iIPbFVQ2@UFI{^C47=CqYXY6BAch%(DwX1R z%404*sbf1wqAnhmzASdq74}`r0)w}~QUXl67!$ukm_ufV%sCeLc3q4;SIYKSLoq`5 zK~|R@WNzeR*l?w)wFj-S<`mb*I96T`VqMk0p)=QvH7v9AI~l9JNStMT&Ip-q>sYON zk$t)}S*)|!FYGE$ar`{9MzFa{Dr~kHEJ1RyELba)R0$%HJ^>Okd%qCTO7uOffp-GSEPAt{IeeQz*_`?w T*;wO{2P1kP%7xKsok>Q)EAPU3X^e%{MTE*Uh7i};1}&b{ZHdw$(>lgd8l=AFx)7P(R9 zDtYI&pN}T=K^)rpyj?)H_4-K>o^k*2uXKRBKPoz`f*y(wx-1=u-QS6%=UIRJcE6Rx;D!qJBu#yTk6Zu z9Yhfz^y&RrPebgnxLI4>sNXKnXmMsWzBRgX>AK|jwff~giAe);B7@b`p4=;#4DNkS z_H;01r^w2I)Agx8JKemdL&VxI!ZDoo;?!i4_cE0@(ZPZD zSf(Jkk$6d8s}axC9rgm*iNIs&LVEEMC-6S9Wm0Z*XRTF^FIJ}GnRCgYlqcZ$jXhs1+0vti{)9O47Zg}OT${5Rif$!j4Obo3&$*^y2DOKM|*$<;u= Kfjv1rO!o`zweKMS literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/Untitled Project.bookmarks.xml b/lab/lab6.si4project/lab6.bookmarks.xml similarity index 95% rename from lab/Untitled Project.si4project/Untitled Project.bookmarks.xml rename to lab/lab6.si4project/lab6.bookmarks.xml index 3b64d5a..c8b9cb1 100644 --- a/lab/Untitled Project.si4project/Untitled Project.bookmarks.xml +++ b/lab/lab6.si4project/lab6.bookmarks.xml @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/lab/lab6.si4project/lab6.sip_sym b/lab/lab6.si4project/lab6.sip_sym new file mode 100644 index 0000000000000000000000000000000000000000..8103e4db0f2077e6e48d0d98e445d6bfb73a0e83 GIT binary patch literal 1467512 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 z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N d0t5&UAV7cs0RjXF5FkK+009C72t0`b{~w3gKvVz# literal 0 HcmV?d00001 diff --git a/lab/lab6.si4project/lab6.sip_xab b/lab/lab6.si4project/lab6.sip_xab new file mode 100644 index 0000000000000000000000000000000000000000..b9486246ea78db2a9ede79fe5312738723cf283e GIT binary patch literal 131072 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 literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/Untitled Project.sip_xc b/lab/lab6.si4project/lab6.sip_xc similarity index 54% rename from lab/Untitled Project.si4project/Untitled Project.sip_xc rename to lab/lab6.si4project/lab6.sip_xc index 95bb2669a8ecb93593894f50e233b197fc947694..5b4224daa6114cc469872e25226be0dfab663f0c 100644 GIT binary patch delta 16369 zcmaib349&Jk@s}Zy!%P-NqV{uk1W}iZP^<6!ZyYp`2YdN$QZ{)!61jrH!|TcA=u*s zEUuuZ1{WcK1%%rH>l0?oM+joWmk^f2-VH|x!6KUw2uZL*fGjL{|JCy(OtRl^`}dBS zp6=@E>guZMs+u?6OndJZT}wKY6~~u zn@^kHHB)N+(n(pzQTEIt|Ae!|BNi>cNhJFkP2nGIl9zt1@hMqnjBa?-@8l!at|hWD z>l~-tC8yEAIpSp1u|3=$G2ai1^ZIHl1a5!b=bR+^C*%QW`W`tBW9Io6zc2eD)H>aq zm30n%z0LBS<~Os>cr?3p_`w%d80Z^fMb;U6Y`vP0x10xEm35v}2liL#xn-SOX>XU9 znRQbA-%MXE=#8jZM9;h|eyO}z28i1L+X*m5%ARu)uG?|F8KjxYpicEy{0+0BhC7AR zKQMNGL@c1@8D^y#SURfz$>s+lVq~`VnSOg_Lxi6Hv9JNNxc};w{s=u-X0~LVHv^pK zfMQM2{`CtVvBc_Z?KCt?sI_uDt+`oLGW`zhPpQX#IPRV^BDD88Q4F+7=*bagqR{ZL zVnK=OzqC5?P#InSf3Qf0LIu=&pGc5LB1u<$BvMisv~iOxp)Xg6YD;>as}4*ZL1#W8 zi?WV|QHp_20goP&MgY%Ki%-m`)P$%SkduLBGVB?6lvd9+XF@K9_tcR{UBY$)B%~o| z#UXQitUjoaHfxz)jG6G(2sk}m}sS~zRW1^gb}E($J&;u-u8^@h=tTOSD`)@ zSM58?a3AK$0~l=`C8~=oLsMU1?-)_R4;&KzB5LW?7LgLJIyg%slu#cmD+b^aDJ88O zVb+s-s;oyvO#Nr14BQkeu??e(`y03zF7s9PVQ$82Qb}H(S*Qj>ag%9(aA7euW<@m3 z?Y-e1hO3+n8FgrJ0q~EDG%bHiRAn1u05Fng5zDcNVW}g9<=BG_s7=x4kHjcVa@<8} zez#d_LKINJnW99UYz?tQo=S!*L7R}ebv@H3jn?^e_HHv}0VScD2a5uJpthZ|OgU~5 z-LYDvOxtKyD`O=rH^TqG72%OQV^0+dgGn=zV3MO)h8I;EPpIRkX|Pj5N=x-kABU|v9?-(7{quZvAU_Y+MRfe-lG%^f zo&QGw|6yj}Wk{$*BF7LeZ5$_T>Uv0|Xu}Az0y8v(+IZ4Ppf>^iMh@|Uhw8g`7OUM; zQ*`uuA{-0Yz%X2@xmK1^UV#~*i|a(8h^kt*N_vIa+M*sOM8|(13Ud4~E$c);*Mz#E zxrir{RD15r&wRxXww|hC%m+uEPz3DLAXY&rZMfisf@Kw9-PBVDoif^cpG;EGer%jW zOG<%k1-?Z+GD@9W%*Qatb?E-m>A-zvoc^mHtk&DkGNJ|`u#6h#l)Cu%k)Wv_yAmuIg&?WqeK&U3m5_zA-WM#! zk4G047t`=9q_)=qt8>~+JgDq(EIRs_$>@}V7R(-F(FpK9HW}Y~JjfGW*rB5^e4K9T zlA!7H*f5Zg^uiC#0;ov6vurx>dr_p?T4THngO8e1vQ7>9uuRwDIlb#Mu`)-*QsyhV z{&<=9|0CdPo&-n>6%k@Ef~i*2^H?1g zeC9B*=x05$=AS6_jUg&wkV1@J4H_JWSw!gYNNmva&lpeRGgiw#kw``%l9rq{M3v&K zlK_OT)8xH&IR)SxfU5u+ak~BIaw^?+qR3Zo&t)YMSGO;#$Don^s`eMdVsy5)2`EoS zpM}f{$MwIwQbx%Bn}Em$ANIDV`pwl>n8Kpp)S1;ZxW|mqPixFlrWC`o)En!zsJq%@ z{T=ITQ2%&L|QafcS z%!Zu#FbRW4DDN6Ft{C_??=OG_>EPpXAiJNUQMicRxq zpa^pwuq>O zH24jMzKXfmKEjlm4rZ$byhl-8n#1HQbUDu-Tqnm1JRL#qW;q>veHrG!G(3&m?}$^G zt40U4Rk`t^fN@FxBX_~>ivFi$vykQXX$*D7z3ICnw;nc!DwzYu>IePlyI(f@f3|0m zNoB3(wXpJn_JeyP^xm@J4rgQ0CiNfK|MxH*IM-a0bryWpZ2v`OLDus-pMSRz19rgCT==>PKzu=2XDFf)BlRVYOjt zL5{a4GhAl!GA&5&Z5xdE&p%<_F8Z8%1AEus`;%jDRr*0zi(sSeNvOnf{D*14G2{N+ zm`MW0149G*?T~SL<~o^RbEs#jmaS}KCH)Nv^RI$F|E-8q@?JBVb@F|f5w58|euyED zw_MmAs^H2pXdG6b5xVn1vyk#8nGye#0`m$UqW}%#YUcPftA~xip`6m^i=@A;$XqPw zK!a$;f{*4Yv3xJ$-Hk5`;7U61l!*Ix6r0}`Jy3D}X|vFsHB!CbU22vI_4mhGFlvJG zQ)XE8E{pqCxp`wmoJ`I4nswx?mNC`s!~rhUfA*()B6Qo&%vp@aW{{LE`{{u9ZG3O{ z9U%rn>iK6fcycmnBSI0UJ%YSpnolNVp{a3I%)q8KrER+}>y=)WDihj}r|`4?4u7>K(A- zk3J#8G@D?NMb$f2*|1utPnyk=ltjZee1l@Ccf49qFh+|zL=jv65vsTqW`MV;2&bTf zAy)P^Xiy9!%JGdtYu%X?rD{Vf%-ki3hzmfj5mH-QtI-rT(Fuk5mWRrq*wAb^f$VzJ zw34v70>jquTsr)k46N>Wp#RX^QXNZcw9x8p53A!h#ORuJW>F+)L7*F&%gEkq#=@3?c|}!rSukA~^;@Tq+QrPG z1ZL9~NfGE9t1cWKG^;~_ER@wQi@^W@f_b3?GARPuQR4ubvNvG(G6<~>DO`Ep2 z&77iHiFIYmY3SsfqJ6L8%-NZ;PR}|nR&#Sstd`JI2hAB-XIdbKSE=3=b>w{n0}WDW$1U3=>CvE1q2u*+2#KxJ(*6gqF z7y|rcjP|AfMLD}D9;}4n#3!5sHR65ric{VrB8HQC1q%lHe1sTHca1fZboP341TD11 z9QxaHay)h~q!-O097zXlaVPz{1`dMmTA{U6BL>LxOGb*T1Rc6qRMVq%q90;&3{bP^ z#(FUo4N_{vOB3jedKf5d9xhspb~8s|A6#)U$TCXYi8fQHWwbb*?7y%wnW(xaH{`?= zY#mKWeMGAQ-c#7F@V&FlwlGCM37W$>^N-Da=-mgywCY!~g}#41w8QPoco$Uyv1z%s zA#A$4N5E)>mdRQFd{!*zaoGAP1hFi7Zkm_?^mv~ZT>&VqrW_Wpb>s^b1zqs1Li7r&6@aGv=4 zCyECn z3EVQynZeZTIg{|j?%8)Ql*vlq;^8|N^s)S3wTNXFb#9YMf7XfOtq3*TE|b~D1~i=F zdy9k@p}{_`iTm)t{2QDX2c?$Q$zdsAV^e3>7gOj$ks4>SOd1kzE;i^fMH*?3Pr!VH zhTM8~mnfyB7m9l-xJnC>;XhPl@U`f%xn`BxJiR=G=Wd$Z4`em1x=6sau{}oDT_h?+ zFa0QPR>K(-XBnnH79F@q*!e>kdPV^xJ$-qRsIV*}YVlFITy6idlqQ}98}ZClVy(nl zbzobq*ql&Tom2>F#Yn6cS6cN(6sP?OMf{`?t|B0HOcC_xYB3wQgpv@9JPrPp@GOjA z?-aB`#-Vbqh4)b@gxqTIv!d5r?9*yV*>$u;sOFVMBjJLp#vI zQrW36($_)b#6jC)=sZT@9>^Bj zJrZZT^-J^ATT|oe&4n#!ogNZNK(qqJ+h&$WtMGsYO>s`}%%JqGCXA;D8jS)fY&gI# z5VoY9$=254DyrU_qy-N<&e+R0d^}>;UXmL7&8n=kjP96ijsl%&(fu%6uUo(pu!;pp z_gII)Lxza1zS{3*e5{+-u~r^2eWCF!4k6AO*K&q`Qv4bb2$2lEO3 zaM%pj;hL@XxO#gU1TI`<_MF+r&M@qQRZj`i^!MesJ}1eil+$x4;|5gnKiw_LLuymA zcJ{(k;yd?==OgO9j=*A4i7wtpyk9M9xoqV`_$Omw2%OqWwD&;5 zI&B`uPFwaI!drxy$LM6}b@)U3+RSqG@RpQ%=iRt)M)|rbsaNfse1M??5chzQw+%v=_;F*Qo zW_1o)4VxdOG;qDS36Eyz{btXZ$786DLuJ%iX@=v0gu<_+f~974Gp<()8#`#6jRuIKU8QX z20=KLP2)!%7RA(^mMPg~(2H~7!X9K7dB^4?yf-BnN_&6l>W>gO&@5LvE5%0wmvN%( zI5VTY+(%}@MDt>-0vnl==)x^U)^+)A;WD z%BeTcv}p8iaDZViT-BAwawR-i%$2?ORg~!S1S60unnE_Uj5RZrX;)VUzd2oE>ncyJ zZ0DWF+dp3|{ASUyDo(cu76EI}O zj%`+TbvW=1GdQ0yR^C1p)#YKE=`QG<-Db(I1UL@n<=9tehor$keYCDS;XZ+zD*0uLUEWN3K|{J&yPaV(wNlY$Oj zFKdfj_n18dG8?X-f!EBcu-;%e()La?VaWkzJ@aV`j-0u$P=sE6l@(=X0yE( z*Uj`NEMGpuLSX@5gT)GF6(deIWhnR<8Dfrz`omfI^!Q#l#XGiH5VsJjt7Lu%>}tb| zTAZO%##w1TRfn0egZ2OFKNtZ9TYLRFSECHu#_-TGc;e{`{u%0NB>67PKe_}GN)sU` zl7f1Vp}E0z?iX7nCwM$^ekV#eP{T*KVoWBct#nUAv$5@u=d)BYEEp3BSq|rG5K)B7 zss;|Nb_@Okl(cHOtO79N zuFqhT(n%+pWl$IJk$~(FW*hs>N)~2n>s1w|jsJt8=?U8~Msi1qsNlvEc*kiIKdG*` zrBGT3VYZ6Nv?XIkdZIX7!^I7oiuX)Xz1^6PSZFxp0XAHGZ|n2HP~jK8A(_i$`dN8ja5em>7;qB_uA zz)El#^*&j}6wT*H76K6|A-8 zq5A+TZ6k(XxdlIZaI8fU`ucGPIV*w!t!A;GXN8ZdeQp@GM3FCDcloUbhAP0KKG z_QCUz$8FF=b0wyebPZcDszJece3R_34OhyJLKkzf^H7OVgBBJwSDD3IgfY;>71G7e zP;opP2pFyfGT{ovPq*;6zZi!WL5Uj6^@WCJoX<}UkJTgeg{z-v zO5oDt;kOzsT-n9EeT19M#;(;P>jW1MO@K6hjw!V^Zd`of42lX&PkL;=cVMO#o%_Bl zgS^=oa@>}T*VD!ZY6<)XLPvC-MMafVh9^QHegAk z&@SsRFgo|ZUjz3Jjbxd_BAodHC2G{RVjH+%rt1##}HfH;!s?#Q<@g zwm}6Our)2fH!fH2FxO^2gIz}@Pbkz3&9tJ3q9zZpK%IB~aMgx5Q zONzDxLIqnDJq2W&%hNU_ZihPZJO=n45=2vo8=&Ym8a=K^Td2}g)|QfsJ3T@C*_Ma4 z1=>htrU7;wKhXWpzy+%~&o*ER^0mwTu%m>QQkdTkR5CR*m8CRnZl=e@dV9F;!k2fl zjeo<~O5Kx1*(6tE<3jDB2wr2f@C;@b6g`rMDvbo{(ov~C|MKLlGa5F4cC=4}rJV2& z>=Ov*?q4q!;WkOBFnf3{DEoD2_ZYp^ZlX^dhV_P=4bcg0KOJpj%AS@_=~8hHj0ku> zPnFF*`*xsX8O-%_Y1c2rdNwAqYzS`F=-8 zZVHq4OK~E-yHBQj+J<7q>|`nPk0j+QVR|DWr~7{|lE^mby+qb$ojLwtTh7CoN~e~x z1AEK8ZDE0FfCVIr(o4aqR&QP9`f5q$(k^2nusH; z4#zj_Ht!JXVi}_Qw}=w|P_Ox!rA}Cp&z{V-QY-5 zF?D!wP%+nxzhC5qdwCd}{_=ZKTH3(y8K@oL6*b~6de&p?oj2`{Yn zYQQ7KzWJ2pLUF2=R-IrLa~TGRAyrFLaei*?MFZCss*SN?+S$jx6_%@!Mk_c?=VqVo zKqGC+a_fL{dzIw;BYJw*&>g^m)a2qaqf_*!x5D18S!Tc@Y)_i#txG4;4 zRu#;wiVB=>P+}^5Zt-NSM6h&0cYYAuGsS=ne|Q0hs43wW`S4>|))H;~3PqROLzgy$rAq0kB8yWSlMz=BWYLidtBCGD z2fprA_sV1#aN+I9;}0h*XGl!b;Qvl13iB)h}O{^cGq*Lg4yl5-;H2t9QS8GVBQn)H;tF?A*lW*IL^=d1RHYw z9}zC^hBFA^5vvO;3wIp?1~Hu6)mFr;JGLYIavVDf#!}}CGR>adIn?kDJZ*bVG^1n{ znv;Uaqe{)JD~cL6k3K{IT%^_3dF<>TnF~Im%0i|?Sq!YD7b~q+E**osKJB!1m&`|7 zKIwCcm;>?@vNWB+(jWdTkABo_){+xO4Z;)%1}wga8$}4o0#S-hWS4sg5NhB)d-iCN zWI-`b6kF0W5KZD-z{y%A+4&}t04#GG_N|48emfGvJL3&|Zi)2V5PJYm-yR9(759#3obpuTg2 znZ{;lc|^<%VbLM+Z@y9@Sj7nud1~S67`?awxuz?wkxwI+r1DxhNxe8RPRp*9`$bET z1phGtP@Lw>xqN5sls^W)AmL;;T%-3bP4mDZYU*YS$)XFoX%A0zMPMtK4t&t;Qk3a?fj?$UJOP4W&=&yxWeIiecp0of#7#4UMmA@Dvl zxygW=qPCmmlVG@7&L#tj50k|&BXDdRk52;(-g3E#gbqWZ;f;p5wY2yDvaN7Sia{b{ zCQSd9l^e)8QGlOesTw4Bnj zZ!>!81k#xZ@M;e2b1FHkI?tbWr+m|*0~c`C-gXSe;Ri%C_X*2?8E-H}L^fOdbk4$O zs~LGoNQgaEI(Q*zoz4lSiftU?eI`hgg$;}t-gHce^Z)9Y%$!~_UB90sdf0qK?}->Y zN(d=o)VQC1#axcW)Zpc#!Crnfi{GWlqgiL*l_X3M@~6r2Pkr4CMRv7;-8j=3d0<0{ zC+zq@m}xafSvc`0kz@wf+75?a*b_QZ0f;)iG>_x|&Etx5Y4Z9YmV~v|7vo4e(x=Bq zsd5Wk$sU5~94E17a%6>10&u%tz+y8D5p7fNZ_JT;Un7t1ottDO$7P}jYH|n-w#xQt z=pD9z212M=YX3fs`4*%Haf@m@um>j zQ$;2vcEkI5y-FcaRe6nBu+@-VkJ748_r;M>zm$;`=M8Q z)-Vbj3}Y#V)RQoqVI_p@mipn?-ufK7tfZGl%Ne=+w2^%B)FD3lY$X=#oNq{E+?)

qcH>C_lDAC{qFI#cyxf5j0K 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 mhfGP?DVgOQq@43_JMlgTdl(-sJ$^B2MLbQDV diff --git a/lab/lab6.si4project/lab6.sip_xm b/lab/lab6.si4project/lab6.sip_xm new file mode 100644 index 0000000000000000000000000000000000000000..b13dd7eb2fbae6ca8139ac8ffc495ad367227ad7 GIT binary patch literal 652 zcmWl|2UiXN006*GLn%pxl7`DauT{2pOSBn-Ge$l_(hr{nD;`?{OG1@?sB)c++&{k z78vhsuRHD?YrJ8C$1JqST8lmICnv14#8M}n@`Uv^_}_IKJ?SZ@4fn1yZn4Q(o1OEs z^DcPWGwwCfeSWdUB+G0y*|TnQ(XW29-6fa3=XY29;ZJ}0+f~n5?tRajVuh*R^pE>p Fa|0`HGn4=T literal 0 HcmV?d00001 diff --git a/lab/Untitled Project.si4project/Untitled Project.sip_xr b/lab/lab6.si4project/lab6.sip_xr similarity index 52% rename from lab/Untitled Project.si4project/Untitled Project.sip_xr rename to lab/lab6.si4project/lab6.sip_xr index 2458b6b732a7cb690ea8cf78f834939be3788a06..6aa5e0a04736b51134304181082b22e9d98f3d80 100644 GIT binary patch delta 20584 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 NVICQ~U?RxM{vV&3@p%9M delta 6015 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

;8} zC~jXrp?G_<9#2%u^7$%>;QR?Uxy(90X1D0+vo*53L3aAAM_R6woIksr+&)&@abtnH zenk_;bnpzh{%%}If4kG(`9w4snI~t>JPAp}!kLrg|C;-|L@W^oB$`mDR3IR1l*#muZ_(O^+S~UH@5g)>w|}a zhk=KKHvqRN;{~a<#nCSCEM@yhwYM9yo%>cF#OB)US(xGIGwxq)O4x6sLnmcof z8}x^pxP3~^>#^*c?Sr+Ie`lq>KDW2=`4X!$z;QZ%9$8u_Kkut)6`LyiXL8_JrT>wR zX2SE;@;rX;3#w~_p3R){gs;w3a~jpY)ViLX?r0Br$o6<8dwb=4+@L><((*SpZ?3ti zcC=c*y1=|GbbEJH*1vS;^6cJDd*{=!bab_2!9L5`p7OpXfA9kE$>i8AyA5Wn4 z_9d!z@~6uD*OEU|+L!$~#J_;}mk|F7{57Qi2IAj>zk~GOL;MGb{|ND)z&}I!Um*S~ z#D9bM?_g>?b7QCF&+SvBj1RMmAzlLUQizv<%fS`kDsUfgUvNKgfA9eCKyW4aMEhb7 z&3`-1@3G4HV|z9e9R%0+*8>j*4*{*yosu3*_d zMlPh*hsGQJ+3|2x4*{P-w%F3ckn^o%@2Xwl=kNFmPyL~mHlVqGu_nxw4cYK^sxt({wF#2 zknEqD=fCWS$^ES7cMSSNkDkB#VzON&3L9U8d&h&!{V5;zcs!%IaI`hO5d3!^55n=G ze|J16VeOyo_Dh0O;54-7pN%Iif%F->^I$En9F*S!>6bx#Id~;_73ALwoQ3q=5MK&j z0p1(D8k`631Kt--An?CCUbjX${{GSU-O*H^GVA_mjvu2;zcU*8 zzsC2@rsG$cb^k|abXnvZ+F$V=jDMY}c;r~6Khv4ZrPo-?L&nK#!N-A*r}~sz*RMGL z2~PiB%nJs6Q|111eL20@)S1(-m+d*G;F-$)-s|?yo29#cw&T3FM$})dl<%eC#Y@1K zf-eK(c<1F*pCar2fj=7myn^EPEOQ*kL$8$i&Hg{faeVYDikDdT>vJ5(ORt9V|7`qp zd$m0py}|dC;~CwalY5`qyS#F)*}mj^seB&c;a@D7?(SFSobR$PQPwy1!%q3a+vJ;9 zu3Vqt*tk!&hg?aeBM`rz;`aS% zoIVQi2OwT7i?ZfFv&5)W+lySH&OfjO{!9`GaJjg;#Toc3561ET2jP0a zL*R$OkATN0%hQ*Qc5kUHKlWJgWM%$+L09z@@Ko?PrTu(CkFk|{{fE3Y#P3v=2d6(> z882XO1L?EIdQnc&weY4f&zzV2-28_M-vPf1eh>UUxJ&LI&FlO0 z_+a=ldHsnx4fJDYC~l9hv9Hn78-tYjThQ!jZj!rNc|6Q#XnfMAd&&acQ~kQ9?c?k} z^YnOqBt-QoF~>)w-!r=|vVI)j*GVtf)0O=#dw;5LKdXPjo+FPB=JR{({UCpvoSqZE z2IAMs>6_1caQ^Ece!bj(oBbV*2b}qn^$UWV!7bpAX-rU#ov2wVAR!{hkFa>w!;^Yp=P!WTH!qQCYnv3W_OmT#_ZF<+c@J# z!r#=~IJcNqFYCu15AkDYQhmiJbN;)N;YVqCj_OCF>h#Ny@2H~gt@X<+Ga?Tu_dEGp zjhcIO`_9qRr{^o<%>h@7=K$sSo}~x&52WdpnfKEQ_Ce$lv6-AA`w&RKP&wWP-2SFV zdGT4lKs20tOxgdl9|u1HeiHl?_-XJn;Ag?lfu9H8pzMDG9(V9HW&h7Uz*%4N*=V#Y zOiRz&KS;cz; z>%jBCUgdZ~^K0x3o)12Q))xEz`7Djco=LXHci3l<%dGcnu+Jt}4BFgWAIay)xH(?T z@pH-ccmn%8vVDrnZsp>vUpAVFEu#Oo-`~OBMcUln*?TGDW$ayL+$@5j-N}m?V{6|@z^T=v>_1gLxbT{a(JJ8u4D1XkNnWOrf*Av+X zsc{-0I)k>C!t}|ztNV+2-r7CX<+@pNeE zM-=Z{nlHecA7tFTKZ{)k@s(QrGqF`#eQEmv?+so}<=aCt zSsE{#s4h=hpqo4W$9y0cY8$Q0FMA8{81RZ^ z3_b*W=s)SVo%-gRTYSNHocYUl_oQ?0I&sn5O6?`rHM5i26Eb3NDC6;L^UfP5{wBl& zPI>tjk6&D%oPUx7m&^G#`=|6ai_BR{`z#H`ayQEUr`g`@dntWC>-`H$L-8(pQqA5T z@=HURB=uCC`yc2Jx5(vXE)Rozwo|@Xnu+D2l-quujC}>U#M)o8-IU*c9|b$8%unlN zA&z(UXZaP;Oe(~?C+oK|luWfL{SkH;90A{{^glWMT}pqGeK)wjeK4eHa(p6qjD7Q# z6<@17e=f@};XS-OOx8uAUBJ77cLVPZ-UDoaZ&Z&@IU{tFdi){Z488??A9V$-O(vi1PKeuS z>$R4T^y~J)i-q^3w>fBNOz@mT= zru4i9^N-{F!b{ahLH_+Ae?+F2|H%9~$bU5CKLGMaDScu6rs;P>{$n8jfsntQ(%bLX zC;}6emwnUkbf=YKTbBawZEbChd_BT zS)O^lgv&b~@*fK67sK?HgHM3`he7@>N?$nNpy{oE{3k;G!y$j1()Y3MH{klNg#0H# z{v#lNg3{aXOXB>iApgmb|47K6l=%zmyRjGKKLzq11^H8y-hSTzPk(R7f0Ob)RgT{- zuWwoBljNJFo$coq<$VymeVq*XPlWg>P~J&U-l9qmO}QQr3}w5*;0QPhZU=XOJHavVVsIBY4o-lRV0wXY;e3ndCkOt1H+VHT58em7FL*!j{@??^jmq&Z=yv;WmAgK3ea(f_u_iTM z&pmIDUuHA7S4Kz6*ShvORM8 z`<3H8`(B7Y0P%Gcx4Sf){y~T@l=~ZNdBoe=Yiaou(#PA3Hs$(oj(oRVe&+R>96d0! zr*l3ea;c6EztevCpwBO!r0JKL@1M=*GNJSb&iTIxl0Q_=-`O95KL&pS{uKNf_;c_V z;4i^nfxiZS1O68L9r!7l{}Stdcb@;J$(7dgb?j%z!g_v;y`gfvU_UG4V>yvvkAV2L z&hdoWw>$crvwr7uS&>dC;{`byUpQVFPYBZZLYHn|Qcs`$UftddSN%Qe`fh|WKWOpl zrXO|tf6`o2{j+Y*FW_Inzv=e=uDRAnr|yOO1A<1KuSm1EzF2dEyF_ziL#gJb+A__) zx^m3{Plax8rS4#r?nU=%?Uf6!1NYI=2LmrT+mC3b7qm@MuAgCM)%;@wfs9p;f3aBZFW6fs{Xh1PO8y#8b#q^3dNrO}SG~+> zZc>qGJTyg{pL*|^N_}fQzF@m@y|X6TBRZ7pljMn=;23x@xCuyzF(+c`%w@-n&S5Rh&cT*5I)z4KYbr_SlJ$p8c$1+ zIzO>U>~v@S>wRA{z3=%1b^H5Ycwh5nN`EOjdD5f{s%Z1Kejf6LRnGY)`y%kg;H$t_ zgRcQ!3)8y};@5+30N)0_9K0KNSMWS#edYOUSNb#T4)Bf2{V^QBQfWVSH>6(-u7l~F z3GuVQXM@w=rQqe@72y5Bhk}mV?v&oX{>tflAie~g0cXKE@LI5X{^6nN9S89vz^8&w1D_7w0~`iNzKyf63! zX#Y60e@w=Q%RK61%C4~4AExxiae98^C<@vAZH^xg@uOwdezLtdegec#aqh3oC(QnY*L!c!+Ap2CLyP?ch6M`ghU%mst0!CeqpH-DLau zCiWf9_yVn;xy(_@`A8yd#O|T={lpA&{U+Zi?>8{}XB1ByuUxN9WIM$9&h?8t`*et3 z2EGP-6Zl%)Q*H*&RF-FwK9G2{vwi0q=>ts1IPZJOC+P!C_bShGu{X7EzM(m6tnvE$ z*&DkM`9`{ad6_)lHm^q{>3-77<@vF^2qBWuWw|Sy^V@tf)-Et2G#mB38UF* zACXLD60@BAB1tdI2`JCMv1dbkV`cd^`f5CzXbyTFF12ok;Q68VNerc-5&QmLexK7L z%KF0bN5PMQ9|u1HeiHnYvORG5Pec3}@UxKqIfy?GegXU<^e+Bu!hWIz& zZz2765dQ)EBlsuq&){Fczk7%tO^x)v_us+P*)cmCynR!=2wV&<0hfZyz~$fya3#13JeuaO z+`MFzPtyHqEzbT=^uFJ1o@b!lS*7Kd>dvLNRMrP}PU(*{{SDv$HkRhc{@x0||Lw1S zAKW-uzPSnf(fi@HE~Cj?KVK-5+s1zRI{l439y|d&5xgyU5;&q<&!hLzO~v=q&63NP zE~?Nk9gZ2ZmHi8Qdy3oN*JSSi@n{+CPOKl5Z?x;vGddvN3GoCKVtNq3>s zkG&YucY)*J1eiWxZtmQuJg#pFoCYreXTVu-4%`jy0WSqF11|@!0IvkE0`CRh8@w8v z2k!&RZ(oS-2i_li0Qf-gLEwYIhrsj?h4^9M!@);@j|3kDJ{o)s_*n27@LKS3V4t%6 z^ZL9%*dU4Eaw1p9($=@}CYq1Ja)fJ`2*H4L%2aF8Dm~`QQt{ z7lJPWUkttkd@1-c@a5nuz*mB=0?&p0<<-z$*FgGfA$}diuLs`%z7c#A_-61e;9J4B zfo})j0lpJ_7x-@QJ>Yx6>%jMc?*~6nMu#iw$J^6`5Pt~#F!&Mhqu|HDkAt58KM8&c z{51F(@U!6OVEuR=;xB-Ag7wb@u9nAt^ZEg|ms{FwLUs+<1N$#S8L#B@dxAsYPTAEk zpHJiTZOZr#`#fblg^$n6zyX+k5cZeN;1=*ga4UEbco*=lU@x^#h4s7$*MB$Yzq|;` z>m^t|FN0r!^shqvHSp`;Hz5C;;I|SniXORALh<^e864HMK@vp((fWHNQ2mT)X1NcYqPk*sX%Ju$vX}RA2 zErWPDxB}8wLc9vx2h#V2ct42uhxh>SKuA9bydHQkcnFlYKE#KDhk=KKH-Pdsg!l-E zZv^p;!JB|bLjFx5z8QEFq~9FkqrqE%$AGs4j|GndZw1~OybX9fcmjALcw6u!@MQ25 z@Ko?L@OI$o;2GeV;920=;O)UXfOiDX0nY{R1a^U|!ESI3*aNNw*MaAOz2Kd}^TGAt z25=*|3G4$e0QYx6>%jMc?*~5seh~Z+_+juP;77rafgcAy z0e%wv6!>ZIGvH^z&w-x@zW~R}7a{%<_+{`b;8($~fnNu|0e%zw795Y>hWfq(>E8vv zSMH3D@$vtCYp|9t>9Icme+d2vrvEYc6Y!_t&!9e^gTDZO3H}QFHI)Ai#J>f92kF0u z_V@wj_ebzg;Gd!VU!c5SA^pGmy{dK2{Y&Cs`F@p$?q4dg-UpcOh{Qt5^FQoA`@Yq0 z<;S5Hg=w8&->_m zS({P*vO!yLe)&DEO_b-?IX)7+DR`9fJfhF*^WgWmHkb9Y#($~2(PaCU4ud|}b)IrR zTe>SJE(V|F+&}RTea~xawSCe_<0_gSpTY1iouLQncz#RiEo9QML?^kj)O?>Fz3wh? zzVke!{2o}+X|F&0K3Ga=56YiRga6s@hb@8p_&u>{%Jcs`y-Ve+na|s?w}bdnb$y`t zGT9#H5H+V?PPRX}$$nfGWnHf)4_B|Blb@h?iS_&+r{4hLS5tZR`zOIJaSm}S!=hwxMs>1!gdG;etT#$|Row)RDxvI%7 zaz2)96Myyhgtphpj|=|3P^tBN$3OCYp>x#w)AxpUkmbqspX!@!+fjFXj_#!+mHs8a z&Sg{GQ#aE+eU$E*o9j-`mF1i7D~Kg~;yX#(?YC6?w|x)kdFA~;UizNV3*Z;QfA;;P zmmvMi;D7e}O0PivSHZpCUlN7;U-|n*f4A>1y{0Tru0MW{X|!^`5XW8Mhm_}^IDU$A z{wlu*)!S|DZGqkv=xu@C7U*q(zoG>;lwC#hep-H>E)LJzjgWEkkj(J}#5a<0`Hy_w zFA4FDDWpEnmxA~vGOKz0k;_j*d?b{=1mc^@xKqEs`}2SMD(`1*q!&m}b@~(e#^(C^ zEuH>?^k|xYYl!53<$1yF<@I3m{3F?u2~|4v6^%x%F0vl=e zS8uF4u!-hC{Yb}DUZ81H$5dXRX*1n{QM!XI#n#Jr`q4IW?!k6Pd-<o!1HDK=KhW1*?MpTxKXdq(*Nz}VS}=L#?5$uTs`GE^jPV z<^TD7t+ISL{UYUk66_tRKE>Aew%NO=<74DGa(=AmQ^>nQ{4qKHMQ@e0=5Lrf{zd8M z%KX;&8F@FzzZ2BYMd{0}@4s<u|d-gt$Y`5nMXs?yvRp7nAdxKYl^Wc48`Rxnw{lNQ!4*>tSJ&(K= z=Ks&k%enX0+XB5U@UOMN`E-2eFI4ldo4)69hSFbRpAWu|(w7T)zn4sZJ{|h)?>}++ zOUd^46WQjMtjzup+q_fC?BB6R$|*bE3_O}_f6tcFA1C8Zc_)+Y>kAw|m0W7|&)8>^ z%f(oki+!Q=xPrHpy%ndt$>cKY{q3B7s*IccSN3+0emcZwLVOm)XOr#wo4C9kApMTg zPJ7G&*US9o{d%0g0pg9|bHNA8{+ctt=ab6?x3anXFC^RNM_m3jWc&P#eFwQpY*k2q zkIZi_WsYA)Rj;(Zcg((mTyBl8vbUh7w7cl+fwVr^=SS>8(oT6DtueXy>5|q+T%8I zKQXnC{w}ipy)&NP3_4%zW8II(-i2&Guf;B>^gPG>>rTc~uPEok%|2IMH7$Q$WBAvd z4Ru^Do7(Y};3ud()%0hU(B#ZtzB%Bj*;F~dYo-reZ)UG7l!iSDyg7I@xK1ulIZ>)# zG!s_NU)b{~RA>)!1ma%he2de+s?L8P=z0zOx@!7>)hEjRt$Upl%;p?62Px%&?h}{Za z&uQO$Pbe<7QoeUh-y05XtvhNyd1r3V|H}7?x2d#NR$8AZe|)@Nes-pE|7%Y;or%j; z$K3z3v(o1Fzz!(O=a0U3JWDN)zIS{%t?l;liobt6TaD8Vv3V+g{P-i}7STiAUX9ZS z%=e+Vy?yqO@%zcsY5TQ40ZiXV{_DS|e5~@m%!Tv3!6%jVm;LX4Z@HD~Z||@9`^&d0 z^S?BcO5Q)Cc{%D1s{x0|)G}@ka?|*w+ptl8jTcEcE{;>z#CRQ@DSRuV)v5i@_!EJYgxs%OGA3u7LEF5U&FFf%JVL-VfsaAwB>+5Yi6< zuLm9s9s=d95AmVkVc_B54WPUYAwB})8$oMkmFL-D0d~iLu0o({~0{g%Vz8uAUBJ77 zcLVPZ-UDoa_XPL8|3^O_E{68$0>{A#a1xvXr@>3W8SsBrZ>D!{dRw5k1$tYcw*`7z zptlA7krvof`F>C^lOt1H+VHT58em7FL*!j{@??^jmr2;(Czl$Dn~QSA(&h^ z9cxnKHE#dm%6JkDkNS=PU*e1(il8SDT*JfZ)-Tr?%bxCxR|@ud&iDDG?^f=w&c(uA zK6UzDw|{|S5%e~A1}pbh=38m0Ieul1cXImsmGLn4y%2u@;_E1Ge}bOVKM3)Ka%jw29`UyJS~Zc4&gq|(@v(*Y2#9Z6RrsD6wQqOyIcNRO=dvPwiSm8*+%h9_wDbIs2(}tE$LRK5 zs;5tXuWoOKtNtE!eK$gxAGCON(~r9SKWVP1{#m!@7x1s(-*kI_*IetPn~@6Jf6%D& z6>0X?7i(^CmuPNmDAn9lTc+7pSFSnWsnG4M)E%tSz35)8y>j7o;67UVVBjTZ`w`9b z{mea_q4gTAe*nUhyd7R3#4wvV8$qeQkR_r<=$>JA2OBUqKv3E$5n!?4HC(I>jfrqI0|M%Wd?lafVIqqe4&Q92Fpz;nYGgn4XTBrP95Ov%RRU>C>f>FnrCRsVo-t&n5MOXnI2867uqE*#m1?15jnAikycyn6oZHoLdf{^pj*4+D;n-9%%<+Y@`uiN8 zMgH~O!Ep`&B>K}aj&n4Q<5gZVrd8qCtV4FrrCq-RUO?xJ7QfW3Is(SELXJ)OVm48> znU%tEDsu5t06J8hLc87_pD$C;7}pYxUdpwJ(B~l>9|A{?Arr&{dvLsVoMZeSs{F;c zR=_cra+fwVm$McJ>`z-WFLRvXcuhVy__{~Kl5+-iNH~_<;u5n7S-y6>KhE5Nxl|E- zWEPO(FW!-Za92MhQ>s`=bPXryVRnUK`(XJWO3ON>w`Jz>ll8+7GSO-9Ri9f+QS~W4Q6>w}y zS%rx8oqO|s9eyb|P6nWn;uSUK*Nkh09PN(QVgEP`14tQwxK=^0WARu;jA;cNTN+bE zH*GX0rCs+zJDvkTTq~n{vJk2w#4>pc{v zH8C-+6>!WZGMRkRCdx50&hh)lXOWL }DYgls5m{!Qq&1Mp9`Bsai_!kfMoV8(M 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 - - - - - - - - + + + + + + + + + diff --git a/lab/lab6.si4project/lab6.siwork b/lab/lab6.si4project/lab6.siwork new file mode 100644 index 0000000000000000000000000000000000000000..ec15da2ffda1614f02aa64bc97d57783306eea4b GIT binary patch literal 90385 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 sizeof (fsipcbuf.write.req_buf)) n = sizeof (fsipcbuf.write.req_buf); @@ -152,6 +153,7 @@ devfile_write(struct Fd *fd, const void *buf, size_t n) return fsipc(FSREQ_WRITE, NULL); // panic("devfile_write not implemented"); + } static int diff --git a/lab/lib/malloc.c b/lab/lib/malloc.c new file mode 100644 index 0000000..6e5ac3e --- /dev/null +++ b/lab/lib/malloc.c @@ -0,0 +1,140 @@ + +#include + +/* + * Simple malloc/free. + * + * Uses the address space to do most of the hard work. + * The address space from mbegin to mend is scanned + * in order. Pages are allocated, used to fill successive + * malloc requests, and then left alone. Free decrements + * a ref count maintained in the page; the page is freed + * when the ref count hits zero. + * + * If we need to allocate a large amount (more than a page) + * we can't put a ref count at the end of each page, + * so we mark the pte entry with the bit PTE_CONTINUED. + */ +enum +{ + MAXMALLOC = 1024*1024 /* max size of one allocated chunk */ +}; + +#define PTE_CONTINUED 0x200 + +static uint8_t *mbegin = (uint8_t*) 0x08000000; +static uint8_t *mend = (uint8_t*) 0x10000000; +static uint8_t *mptr; + +static int +isfree(void *v, size_t n) +{ + uintptr_t va, end_va = (uintptr_t) v + n; + + for (va = (uintptr_t) v; va < end_va; va += PGSIZE) + if (va >= (uintptr_t) mend + || ((uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P))) + return 0; + return 1; +} + +void* +malloc(size_t n) +{ + int i, cont; + int nwrap; + uint32_t *ref; + void *v; + + if (mptr == 0) + mptr = mbegin; + + n = ROUNDUP(n, 4); + + if (n >= MAXMALLOC) + return 0; + + if ((uintptr_t) mptr % PGSIZE){ + /* + * we're in the middle of a partially + * allocated page - can we add this chunk? + * the +4 below is for the ref count. + */ + ref = (uint32_t*) (ROUNDUP(mptr, PGSIZE) - 4); + if ((uintptr_t) mptr / PGSIZE == (uintptr_t) (mptr + n - 1 + 4) / PGSIZE) { + (*ref)++; + v = mptr; + mptr += n; + return v; + } + /* + * stop working on this page and move on. + */ + free(mptr); /* drop reference to this page */ + mptr = ROUNDDOWN(mptr + PGSIZE, PGSIZE); + } + + /* + * now we need to find some address space for this chunk. + * if it's less than a page we leave it open for allocation. + * runs of more than a page can't have ref counts so we + * flag the PTE entries instead. + */ + nwrap = 0; + while (1) { + if (isfree(mptr, n + 4)) + break; + mptr += PGSIZE; + if (mptr == mend) { + mptr = mbegin; + if (++nwrap == 2) + return 0; /* out of address space */ + } + } + + /* + * allocate at mptr - the +4 makes sure we allocate a ref count. + */ + for (i = 0; i < n + 4; i += PGSIZE){ + cont = (i + PGSIZE < n + 4) ? PTE_CONTINUED : 0; + if (sys_page_alloc(0, mptr + i, PTE_P|PTE_U|PTE_W|cont) < 0){ + for (; i >= 0; i -= PGSIZE) + sys_page_unmap(0, mptr + i); + return 0; /* out of physical memory */ + } + } + + ref = (uint32_t*) (mptr + i - 4); + *ref = 2; /* reference for mptr, reference for returned block */ + v = mptr; + mptr += n; + return v; +} + +void +free(void *v) +{ + uint8_t *c; + uint32_t *ref; + + if (v == 0) + return; + assert(mbegin <= (uint8_t*) v && (uint8_t*) v < mend); + + c = ROUNDDOWN(v, PGSIZE); + + while (uvpt[PGNUM(c)] & PTE_CONTINUED) { + sys_page_unmap(0, c); + c += PGSIZE; + assert(mbegin <= c && c < mend); + } + + /* + * c is just a piece of this page, so dec the ref count + * and maybe free the page. + */ + ref = (uint32_t*) (c + PGSIZE - 4); + if (--(*ref) == 0) + sys_page_unmap(0, c); +} + diff --git a/lab/lib/nsipc.c b/lab/lib/nsipc.c new file mode 100644 index 0000000..420c9e3 --- /dev/null +++ b/lab/lib/nsipc.c @@ -0,0 +1,123 @@ +#include +#include +#include + +#define debug 0 + +// Virtual address at which to receive page mappings containing client requests. +#define REQVA 0x0ffff000 +union Nsipc nsipcbuf __attribute__((aligned(PGSIZE))); + +// Send an IP request to the network server, and wait for a reply. +// The request body should be in nsipcbuf, and parts of the response +// may be written back to nsipcbuf. +// type: request code, passed as the simple integer IPC value. +// Returns 0 if successful, < 0 on failure. +static int +nsipc(unsigned type) +{ + static envid_t nsenv; + if (nsenv == 0) + nsenv = ipc_find_env(ENV_TYPE_NS); + + static_assert(sizeof(nsipcbuf) == PGSIZE); + + if (debug) + cprintf("[%08x] nsipc %d\n", thisenv->env_id, type); + + ipc_send(nsenv, type, &nsipcbuf, PTE_P|PTE_W|PTE_U); + return ipc_recv(NULL, NULL, NULL); +} + +int +nsipc_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + int r; + + nsipcbuf.accept.req_s = s; + nsipcbuf.accept.req_addrlen = *addrlen; + if ((r = nsipc(NSREQ_ACCEPT)) >= 0) { + struct Nsret_accept *ret = &nsipcbuf.acceptRet; + memmove(addr, &ret->ret_addr, ret->ret_addrlen); + *addrlen = ret->ret_addrlen; + } + return r; +} + +int +nsipc_bind(int s, struct sockaddr *name, socklen_t namelen) +{ + nsipcbuf.bind.req_s = s; + memmove(&nsipcbuf.bind.req_name, name, namelen); + nsipcbuf.bind.req_namelen = namelen; + return nsipc(NSREQ_BIND); +} + +int +nsipc_shutdown(int s, int how) +{ + nsipcbuf.shutdown.req_s = s; + nsipcbuf.shutdown.req_how = how; + return nsipc(NSREQ_SHUTDOWN); +} + +int +nsipc_close(int s) +{ + nsipcbuf.close.req_s = s; + return nsipc(NSREQ_CLOSE); +} + +int +nsipc_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + nsipcbuf.connect.req_s = s; + memmove(&nsipcbuf.connect.req_name, name, namelen); + nsipcbuf.connect.req_namelen = namelen; + return nsipc(NSREQ_CONNECT); +} + +int +nsipc_listen(int s, int backlog) +{ + nsipcbuf.listen.req_s = s; + nsipcbuf.listen.req_backlog = backlog; + return nsipc(NSREQ_LISTEN); +} + +int +nsipc_recv(int s, void *mem, int len, unsigned int flags) +{ + int r; + + nsipcbuf.recv.req_s = s; + nsipcbuf.recv.req_len = len; + nsipcbuf.recv.req_flags = flags; + + if ((r = nsipc(NSREQ_RECV)) >= 0) { + assert(r < 1600 && r <= len); + memmove(mem, nsipcbuf.recvRet.ret_buf, r); + } + + return r; +} + +int +nsipc_send(int s, const void *buf, int size, unsigned int flags) +{ + nsipcbuf.send.req_s = s; + assert(size < 1600); + memmove(&nsipcbuf.send.req_buf, buf, size); + nsipcbuf.send.req_size = size; + nsipcbuf.send.req_flags = flags; + return nsipc(NSREQ_SEND); +} + +int +nsipc_socket(int domain, int type, int protocol) +{ + nsipcbuf.socket.req_domain = domain; + nsipcbuf.socket.req_type = type; + nsipcbuf.socket.req_protocol = protocol; + return nsipc(NSREQ_SOCKET); +} diff --git a/lab/lib/sockets.c b/lab/lib/sockets.c new file mode 100644 index 0000000..7a8c818 --- /dev/null +++ b/lab/lib/sockets.c @@ -0,0 +1,132 @@ +#include +#include + +static ssize_t devsock_read(struct Fd *fd, void *buf, size_t n); +static ssize_t devsock_write(struct Fd *fd, const void *buf, size_t n); +static int devsock_close(struct Fd *fd); +static int devsock_stat(struct Fd *fd, struct Stat *stat); + +struct Dev devsock = +{ + .dev_id = 's', + .dev_name = "sock", + .dev_read = devsock_read, + .dev_write = devsock_write, + .dev_close = devsock_close, + .dev_stat = devsock_stat, +}; + +static int +fd2sockid(int fd) +{ + struct Fd *sfd; + int r; + + if ((r = fd_lookup(fd, &sfd)) < 0) + return r; + if (sfd->fd_dev_id != devsock.dev_id) + return -E_NOT_SUPP; + return sfd->fd_sock.sockid; +} + +static int +alloc_sockfd(int sockid) +{ + struct Fd *sfd; + int r; + + if ((r = fd_alloc(&sfd)) < 0 + || (r = sys_page_alloc(0, sfd, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) { + nsipc_close(sockid); + return r; + } + + sfd->fd_dev_id = devsock.dev_id; + sfd->fd_omode = O_RDWR; + sfd->fd_sock.sockid = sockid; + return fd2num(sfd); +} + +int +accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + int r; + if ((r = fd2sockid(s)) < 0) + return r; + if ((r = nsipc_accept(r, addr, addrlen)) < 0) + return r; + return alloc_sockfd(r); +} + +int +bind(int s, struct sockaddr *name, socklen_t namelen) +{ + int r; + if ((r = fd2sockid(s)) < 0) + return r; + return nsipc_bind(r, name, namelen); +} + +int +shutdown(int s, int how) +{ + int r; + if ((r = fd2sockid(s)) < 0) + return r; + return nsipc_shutdown(r, how); +} + +static int +devsock_close(struct Fd *fd) +{ + if (pageref(fd) == 1) + return nsipc_close(fd->fd_sock.sockid); + else + return 0; +} + +int +connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + int r; + if ((r = fd2sockid(s)) < 0) + return r; + return nsipc_connect(r, name, namelen); +} + +int +listen(int s, int backlog) +{ + int r; + if ((r = fd2sockid(s)) < 0) + return r; + return nsipc_listen(r, backlog); +} + +static ssize_t +devsock_read(struct Fd *fd, void *buf, size_t n) +{ + return nsipc_recv(fd->fd_sock.sockid, buf, n, 0); +} + +static ssize_t +devsock_write(struct Fd *fd, const void *buf, size_t n) +{ + return nsipc_send(fd->fd_sock.sockid, buf, n, 0); +} + +static int +devsock_stat(struct Fd *fd, struct Stat *stat) +{ + strcpy(stat->st_name, ""); + return 0; +} + +int +socket(int domain, int type, int protocol) +{ + int r; + if ((r = nsipc_socket(domain, type, protocol)) < 0) + return r; + return alloc_sockfd(r); +} diff --git a/lab/lib/spawn.c b/lab/lib/spawn.c index dd1eb43..fd47bc9 100644 --- a/lab/lib/spawn.c +++ b/lab/lib/spawn.c @@ -101,6 +101,7 @@ spawn(const char *prog, const char **argv) // Create new child environment if ((r = sys_exofork()) < 0) return r; + // 父进程返回子进程的PID child = r; @@ -305,6 +306,7 @@ static int copy_shared_pages(envid_t child) { // LAB 5: Your code here. + size_t pn; int r; struct Env *e; @@ -317,6 +319,7 @@ copy_shared_pages(envid_t child) } } } + return 0; } diff --git a/lab/lib/syscall.c b/lab/lib/syscall.c index d8b1987..fce87c6 100644 --- a/lab/lib/syscall.c +++ b/lab/lib/syscall.c @@ -117,3 +117,22 @@ sys_ipc_recv(void *dstva) return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0); } +unsigned int +sys_time_msec(void) +{ + return (unsigned int) syscall(SYS_time_msec, 0, 0, 0, 0, 0, 0); +} + +int +sys_pkt_try_send(void *buf, size_t len) +{ + return syscall(SYS_pkt_try_send, 1, (uint32_t)buf, (uint32_t)len, 0, 0, 0); +} + + +int +sys_pkt_try_receive(void *rev_buf, size_t *len) +{ + return syscall(SYS_pkt_try_recv, 1, (uint32_t)rev_buf, (uint32_t)len, 0, 0, 0); +} + diff --git a/lab/net/Makefrag b/lab/net/Makefrag new file mode 100644 index 0000000..ea7f918 --- /dev/null +++ b/lab/net/Makefrag @@ -0,0 +1,27 @@ + +include net/lwip/Makefrag + +NET_SRCFILES := net/timer.c \ + net/input.c \ + net/output.c + +NET_OBJFILES := $(patsubst net/%.c, $(OBJDIR)/net/%.o, $(NET_SRCFILES)) + +$(OBJDIR)/net/%.o: net/%.c net/ns.h $(OBJDIR)/.vars.USER_CFLAGS $(OBJDIR)/.vars.NET_CFLAGS + @echo + cc[USER] $< + @mkdir -p $(@D) + $(V)$(CC) -nostdinc $(USER_CFLAGS) $(NET_CFLAGS) -c -o $@ $< + +$(OBJDIR)/net/ns: $(OBJDIR)/net/serv.o $(NET_OBJFILES) $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a $(OBJDIR)/lib/liblwip.a user/user.ld + @echo + ld $@ + $(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib \ + $(OBJDIR)/lib/entry.o $< $(NET_OBJFILES) \ + -L$(OBJDIR)/lib -llwip -ljos $(GCC_LIB) + $(V)$(OBJDUMP) -S $@ >$@.asm + +$(OBJDIR)/net/test%: $(OBJDIR)/net/test%.o $(NET_OBJFILES) $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a $(OBJDIR)/lib/liblwip.a user/user.ld + @echo + ld $@ + $(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib \ + $(OBJDIR)/lib/entry.o $< $(NET_OBJFILES) \ + -L$(OBJDIR)/lib -llwip -ljos $(GCC_LIB) + $(V)$(OBJDUMP) -S $@ >$@.asm diff --git a/lab/net/input.c b/lab/net/input.c new file mode 100644 index 0000000..a6b5b60 --- /dev/null +++ b/lab/net/input.c @@ -0,0 +1,53 @@ +#include "ns.h" +#include + + +extern union Nsipc nsipcbuf; + + +void +sleep(int msec) +{ + unsigned now = sys_time_msec(); + unsigned end = now + msec; + + if ((int)now < 0 && (int)now > -MAXERROR) + panic("sys_time_msec: %e", (int)now); + if (end < now) + panic("sleep: wrap"); + + while (sys_time_msec() < end) + sys_yield(); +} + + + + +void +input(envid_t ns_envid) +{ + binaryname = "ns_input"; + + // LAB 6: Your code here: + // - read a packet from the device driver + // - send it to the network server + // Hint: When you IPC a page to the network server, it will be + // reading from it for a while, so don't immediately receive + // another packet in to the same physical page. + size_t len; + char rev_buf[RX_PACKET_SIZE]; + size_t i = 0; + while(1) { + // 阻塞式读 + while ( sys_pkt_try_receive(rev_buf, &len) < 0) { + sys_yield(); + } + memcpy(nsipcbuf.pkt.jp_data, rev_buf, len); + nsipcbuf.pkt.jp_len = len; + + ipc_send(ns_envid, NSREQ_INPUT, &nsipcbuf, PTE_P|PTE_U); + sleep(50); + } + +} + diff --git a/lab/net/lwip/FILES b/lab/net/lwip/FILES new file mode 100644 index 0000000..952aeab --- /dev/null +++ b/lab/net/lwip/FILES @@ -0,0 +1,13 @@ +api/ - The code for the high-level wrapper API. Not needed if + you use the lowel-level call-back/raw API. + +core/ - The core of the TPC/IP stack; protocol implementations, + memory and buffer management, and the low-level raw API. + +include/ - lwIP include files. + +netif/ - Generic network interface device drivers are kept here, + as well as the ARP module. + +For more information on the various subdirectories, check the FILES +file in each directory. diff --git a/lab/net/lwip/Makefrag b/lab/net/lwip/Makefrag new file mode 100644 index 0000000..3cc96e1 --- /dev/null +++ b/lab/net/lwip/Makefrag @@ -0,0 +1,73 @@ +LWIP_INCLUDES := \ + -I$(TOP)/net/lwip/include \ + -I$(TOP)/net/lwip/include/ipv4 \ + -I$(TOP)/net/lwip/ext \ + -I$(TOP)/net/lwip/jos \ + -I$(TOP)/inc/ + +OBJDIRS += \ + net/lwip \ + net/lwip/api \ + net/lwip/core \ + net/lwip/core/ipv4 \ + net/lwip/netif \ + net/lwip/jos/arch \ + net/lwip/jos/jif \ + net/lwip/jos/api + +USER_INC += $(LWIP_INCLUDES) + +LWIP_SRCFILES += \ + net/lwip/api/api_lib.c \ + net/lwip/api/api_msg.c \ + net/lwip/api/err.c \ + net/lwip/api/sockets.c \ + net/lwip/api/tcpip.c \ + net/lwip/api/netbuf.c \ + net/lwip/core/init.c \ + net/lwip/core/tcp_in.c \ + net/lwip/core/dhcp.c \ + net/lwip/core/mem.c \ + net/lwip/core/memp.c \ + net/lwip/core/netif.c \ + net/lwip/core/pbuf.c \ + net/lwip/core/raw.c \ + net/lwip/core/stats.c \ + net/lwip/core/sys.c \ + net/lwip/core/tcp.c \ + net/lwip/core/ipv4/ip_addr.c \ + net/lwip/core/ipv4/icmp.c \ + net/lwip/core/ipv4/ip.c \ + net/lwip/core/ipv4/ip_frag.c \ + net/lwip/core/ipv4/inet_chksum.c \ + net/lwip/core/ipv4/inet.c \ + net/lwip/core/tcp_out.c \ + net/lwip/core/udp.c \ + net/lwip/netif/etharp.c \ + net/lwip/netif/loopif.c \ + net/lwip/jos/arch/sys_arch.c \ + net/lwip/jos/arch/thread.c \ + net/lwip/jos/arch/longjmp.S \ + net/lwip/jos/arch/perror.c \ + net/lwip/jos/jif/jif.c \ +# net/lwip/jos/jif/tun.c \ + net/lwip/jos/api/lsocket.c \ + net/lwip/jos/api/lwipinit.c + +LWIP_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(LWIP_SRCFILES)) +LWIP_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(LWIP_OBJFILES)) + +$(OBJDIR)/net/lwip/%.o: net/lwip/%.c $(OBJDIR)/.vars.USER_CFLAGS + @echo + cc[USER] $< + $(V)mkdir -p $(@D) + $(V)$(CC) -nostdinc $(USER_CFLAGS) $(USER_INC) -c -o $@ $< + +$(OBJDIR)/net/lwip/%.o: net/lwip/%.S $(OBJDIR)/.vars.USER_CFLAGS + @echo + as[USER] $< + @mkdir -p $(@D) + $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $< + +$(OBJDIR)/lib/liblwip.a: $(LWIP_OBJFILES) + @echo + ar $@ + $(V)mkdir -p $(@D) + $(V)$(AR) r $@ $(LWIP_OBJFILES) diff --git a/lab/net/lwip/api/api_lib.c b/lab/net/lwip/api/api_lib.c new file mode 100644 index 0000000..e56016c --- /dev/null +++ b/lab/net/lwip/api/api_lib.c @@ -0,0 +1,571 @@ +/** + * @file + * Sequential API External module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* This is the part of the API that is linked with + the application */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" + +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) +{ + struct netconn *conn; + struct api_msg msg; + + conn = netconn_alloc(t, callback); + + if (conn != NULL ) { + msg.function = do_newconn; + msg.msg.msg.n.proto = proto; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); + + if (conn->err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", conn->op_completed != SYS_SEM_NULL); + LWIP_ASSERT("conn has no recvmbox", conn->recvmbox != SYS_MBOX_NULL); + LWIP_ASSERT("conn->acceptmbox shouldn't exist", conn->acceptmbox == SYS_MBOX_NULL); + sys_sem_free(conn->op_completed); + sys_mbox_free(conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + } + return conn; +} + +/** + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ +err_t +netconn_delete(struct netconn *conn) +{ + struct api_msg msg; + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + msg.function = do_delconn; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + + conn->pcb.tcp = NULL; + netconn_free(conn); + + return ERR_OK; +} + +/** + * Get the type of a netconn (as enum netconn_type). + * + * @param conn the netconn of which to get the type + * @return the netconn_type of conn + */ +enum netconn_type +netconn_type(struct netconn *conn) +{ + LWIP_ERROR("netconn_type: invalid conn", (conn != NULL), return NETCONN_INVALID;); + return conn->type; +} + +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ +err_t +netconn_getaddr(struct netconn *conn, struct ip_addr *addr, u16_t *port, u8_t local) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + msg.function = do_getaddr; + msg.msg.conn = conn; + msg.msg.msg.ad.ipaddr = addr; + msg.msg.msg.ad.port = port; + msg.msg.msg.ad.local = local; + TCPIP_APIMSG(&msg); + + return conn->err; +} + +/** + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY + * to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ +err_t +netconn_bind(struct netconn *conn, struct ip_addr *addr, u16_t port) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_bind; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ +err_t +netconn_connect(struct netconn *conn, struct ip_addr *addr, u16_t port) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_connect; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + /* This is the only function which need to not block tcpip_thread */ + tcpip_apimsg(&msg); + return conn->err; +} + +/** + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return TODO: return value is not set here... + */ +err_t +netconn_disconnect(struct netconn *conn) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_disconnect; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ + struct api_msg msg; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_listen; + msg.msg.conn = conn; +#if TCP_LISTEN_BACKLOG + msg.msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @return the newly accepted netconn or NULL on timeout + */ +struct netconn * +netconn_accept(struct netconn *conn) +{ + struct netconn *newconn; + + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return NULL;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", (conn->acceptmbox != SYS_MBOX_NULL), return NULL;); + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + newconn = NULL; + } else +#else + sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + +#if TCP_LISTEN_BACKLOG + if (newconn != NULL) { + /* Let the stack know that we have accepted the connection. */ + struct api_msg msg; + msg.function = do_recv; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); + } +#endif /* TCP_LISTEN_BACKLOG */ + } + + return newconn; +} + +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @return a new netbuf containing received data or NULL on memory error or timeout + */ +struct netbuf * +netconn_recv(struct netconn *conn) +{ + struct api_msg msg; + struct netbuf *buf = NULL; + struct pbuf *p; + u16_t len; + + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return NULL;); + + if (conn->recvmbox == SYS_MBOX_NULL) { + /* @todo: should calling netconn_recv on a TCP listen conn be fatal (ERR_CONN)?? */ + /* TCP listen conns don't have a recvmbox! */ + conn->err = ERR_CONN; + return NULL; + } + + if (ERR_IS_FATAL(conn->err)) { + return NULL; + } + + if (conn->type == NETCONN_TCP) { +#if LWIP_TCP + if (conn->state == NETCONN_LISTEN) { + /* @todo: should calling netconn_recv on a TCP listen conn be fatal?? */ + conn->err = ERR_CONN; + return NULL; + } + + buf = memp_malloc(MEMP_NETBUF); + + if (buf == NULL) { + conn->err = ERR_MEM; + return NULL; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, conn->recv_timeout)==SYS_ARCH_TIMEOUT) { + conn->err = ERR_TIMEOUT; + p = NULL; + } +#else + sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + + if (p != NULL) { + len = p->tot_len; + SYS_ARCH_DEC(conn->recv_avail, len); + } else { + len = 0; + } + + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (p == NULL) { + memp_free(MEMP_NETBUF, buf); + /* Avoid to lose any previous error code */ + if (conn->err == ERR_OK) { + conn->err = ERR_CLSD; + } + return NULL; + } + + buf->p = p; + buf->ptr = p; + buf->port = 0; + buf->addr = NULL; + + /* Let the stack know that we have taken the data. */ + msg.function = do_recv; + msg.msg.conn = conn; + if (buf != NULL) { + msg.msg.msg.r.len = buf->p->tot_len; + } else { + msg.msg.msg.r.len = 1; + } + TCPIP_APIMSG(&msg); +#endif /* LWIP_TCP */ + } else { +#if (LWIP_UDP || LWIP_RAW) +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, conn->recv_timeout)==SYS_ARCH_TIMEOUT) { + buf = NULL; + } +#else + sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + if (buf!=NULL) { + SYS_ARCH_DEC(conn->recv_avail, buf->p->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err)); + + return buf; +} + +/** + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, struct ip_addr *addr, u16_t port) +{ + if (buf != NULL) { + buf->addr = addr; + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_send(struct netconn *conn, struct netbuf *buf) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %d bytes\n", buf->p->tot_len)); + msg.function = do_send; + msg.msg.conn = conn; + msg.msg.msg.b = buf; + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY (0x01) data will be copied into memory belonging to the stack + * - NETCONN_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write(struct netconn *conn, const void *dataptr, int size, u8_t apiflags) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); + + msg.function = do_write; + msg.msg.conn = conn; + msg.msg.msg.w.dataptr = dataptr; + msg.msg.msg.w.apiflags = apiflags; + msg.msg.msg.w.len = size; + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_close; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + return conn->err; +} + +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param interface the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ +err_t +netconn_join_leave_group(struct netconn *conn, + struct ip_addr *multiaddr, + struct ip_addr *interface, + enum netconn_igmp join_or_leave) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_join_leave_group; + msg.msg.conn = conn; + msg.msg.msg.jl.multiaddr = multiaddr; + msg.msg.msg.jl.interface = interface; + msg.msg.msg.jl.join_or_leave = join_or_leave; + TCPIP_APIMSG(&msg); + return conn->err; +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated struct ip_addr where to store the resolved IP address + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +err_t +netconn_gethostbyname(const char *name, struct ip_addr *addr) +{ + struct dns_api_msg msg; + err_t err; + sys_sem_t sem; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); + + sem = sys_sem_new(0); + if (sem == SYS_SEM_NULL) { + return ERR_MEM; + } + + msg.name = name; + msg.addr = addr; + msg.err = &err; + msg.sem = sem; + + tcpip_callback(do_gethostbyname, &msg); + sys_sem_wait(sem); + sys_sem_free(sem); + + return err; +} +#endif /* LWIP_DNS*/ + +#endif /* LWIP_NETCONN */ diff --git a/lab/net/lwip/api/api_msg.c b/lab/net/lwip/api/api_msg.c new file mode 100644 index 0000000..a655c61 --- /dev/null +++ b/lab/net/lwip/api/api_msg.c @@ -0,0 +1,1210 @@ +/** + * @file + * Sequential API Internal module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + +#include "lwip/memp.h" +#include "lwip/tcpip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" + +/* forward declarations */ +#if LWIP_TCP +static err_t do_writemore(struct netconn *conn); +static void do_close_internal(struct netconn *conn); +#endif + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only references it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ +static u8_t +recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + struct ip_addr *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(addr); + conn = arg; + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) && + ((recv_avail + (int)(p->tot_len)) <= conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) { +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } + } + + if(q != NULL) { + buf = memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + buf->addr = &(((struct ip_hdr*)(q->payload))->src); + buf->port = pcb->protocol; + + SYS_ARCH_INC(conn->recv_avail, q->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_len); + if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + } + } + } + + return 0; /* do not eat the packet */ +} +#endif /* LWIP_RAW*/ + +#if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ +static void +recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); + conn = arg; + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + buf->addr = addr; + buf->port = port; + } + + SYS_ARCH_INC(conn->recv_avail, p->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len); + if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } +} +#endif /* LWIP_UDP */ + +#if LWIP_TCP +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ +static err_t +recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netconn *conn; + u16_t len; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { + return ERR_VAL; + } + + conn->err = err; + if (p != NULL) { + len = p->tot_len; + SYS_ARCH_INC(conn->recv_avail, len); + } else { + len = 0; + } + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) { + return ERR_MEM; + } + + return ERR_OK; +} + +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ +static err_t +poll_tcp(void *arg, struct tcp_pcb *pcb) +{ + struct netconn *conn = arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + + return ERR_OK; +} + +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ +static err_t +sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netconn *conn = arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + + if (conn) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } + + return ERR_OK; +} + +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ +static void +err_tcp(void *arg, err_t err) +{ + struct netconn *conn; + + conn = arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + conn->err = err; + if (conn->recvmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->recvmbox, NULL); + } + if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) { + conn->state = NETCONN_NONE; + sys_sem_signal(conn->op_completed); + } + if (conn->acceptmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->acceptmbox, NULL); + } + if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) { + /* calling do_writemore/do_close_internal is not necessary + since the pcb has already been deleted! */ + conn->state = NETCONN_NONE; + /* wake up the waiting task */ + sys_sem_signal(conn->op_completed); + } +} + +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ +static void +setup_tcp(struct netconn *conn) +{ + struct tcp_pcb *pcb; + + pcb = conn->pcb.tcp; + tcp_arg(pcb, conn); + tcp_recv(pcb, recv_tcp); + tcp_sent(pcb, sent_tcp); + tcp_poll(pcb, poll_tcp, 4); + tcp_err(pcb, err_tcp); +} + +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ +static err_t +accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + struct netconn *newconn; + struct netconn *conn; + +#if API_MSG_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(newpcb->state); +#endif /* TCP_DEBUG */ +#endif /* API_MSG_DEBUG */ + conn = (struct netconn *)arg; + + LWIP_ERROR("accept_function: invalid conn->acceptmbox", + conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;); + + /* We have to set the callback here even though + * the new socket is unknown. conn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); + if (newconn == NULL) { + return ERR_MEM; + } + newconn->pcb.tcp = newpcb; + setup_tcp(newconn); + newconn->err = err; + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + + if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the connection is aborted in tcp_process(), + so do nothing here! */ + newconn->pcb.tcp = NULL; + netconn_free(newconn); + return ERR_MEM; + } + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Create a new pcb of a specific type. + * Called from do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + * @return msg->conn->err, but the return value is currently ignored + */ +static err_t +pcb_new(struct api_msg_msg *msg) +{ + msg->conn->err = ERR_OK; + + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); + + /* Allocate a PCB for this connection */ + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + if(msg->conn->pcb.raw == NULL) { + msg->conn->err = ERR_MEM; + break; + } + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(msg->conn->pcb.udp == NULL) { + msg->conn->err = ERR_MEM; + break; + } +#if LWIP_UDPLITE + if (msg->conn->type==NETCONN_UDPLITE) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (msg->conn->type==NETCONN_UDPNOCHKSUM) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(msg->conn->pcb.tcp == NULL) { + msg->conn->err = ERR_MEM; + break; + } + setup_tcp(msg->conn); + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->conn->err = ERR_VAL; + break; + } + + return msg->conn->err; +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param msg the api_msg_msg describing the connection type + */ +void +do_newconn(struct api_msg_msg *msg) +{ + if(msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + + conn = memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + +#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ + (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) + size = DEFAULT_RAW_RECVMBOX_SIZE; +#else + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + break; + } +#endif + + if ((conn->op_completed = sys_sem_new(0)) == SYS_SEM_NULL) { + memp_free(MEMP_NETCONN, conn); + return NULL; + } + if ((conn->recvmbox = sys_mbox_new(size)) == SYS_MBOX_NULL) { + sys_sem_free(conn->op_completed); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + + conn->acceptmbox = SYS_MBOX_NULL; + conn->state = NETCONN_NONE; + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; + conn->callback = callback; + conn->recv_avail = 0; +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = INT_MAX; +#endif /* LWIP_SO_RCVBUF */ + return conn; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + void *mem; + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + + /* Drain the recvmbox. */ + if (conn->recvmbox != SYS_MBOX_NULL) { + while (sys_mbox_tryfetch(conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { + if (conn->type == NETCONN_TCP) { + if(mem != NULL) { + pbuf_free((struct pbuf *)mem); + } + } else { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(conn->recvmbox); + conn->recvmbox = SYS_MBOX_NULL; + } + + /* Drain the acceptmbox. */ + if (conn->acceptmbox != SYS_MBOX_NULL) { + while (sys_mbox_tryfetch(conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + netconn_delete((struct netconn *)mem); + } + sys_mbox_free(conn->acceptmbox); + conn->acceptmbox = SYS_MBOX_NULL; + } + + sys_sem_free(conn->op_completed); + conn->op_completed = SYS_SEM_NULL; + + memp_free(MEMP_NETCONN, conn); +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ +static void +do_close_internal(struct netconn *conn) +{ + err_t err; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + + /* Set back some callback pointers */ + tcp_arg(conn->pcb.tcp, NULL); + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + /* some callbacks have to be reset if tcp_close is not successful */ + tcp_sent(conn->pcb.tcp, NULL); + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + /* Try to close the connection */ + err = tcp_close(conn->pcb.tcp); + if (err == ERR_OK) { + /* Closing succeeded */ + conn->state = NETCONN_NONE; + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + conn->err = ERR_OK; + /* Trigger select() in socket layer. This send should something else so the + errorfd is set, not the read and write fd! */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + /* wake up the application task */ + sys_sem_signal(conn->op_completed); + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + tcp_sent(conn->pcb.tcp, sent_tcp); + tcp_poll(conn->pcb.tcp, poll_tcp, 4); + tcp_err(conn->pcb.tcp, err_tcp); + tcp_arg(conn->pcb.tcp, conn); + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_delconn(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->state = NETCONN_CLOSE; + do_close_internal(msg->conn); + /* API_EVENT is called inside do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; + } + } + /* tcp netconns don't come here! */ + + /* Trigger select() in socket layer. This send should something else so the + errorfd is set, not the read and write fd! */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + + if (msg->conn->op_completed != SYS_SEM_NULL) { + sys_sem_signal(msg->conn->op_completed); + } +} + +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void +do_bind(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } else { + /* msg->conn->pcb is NULL */ + msg->conn->err = ERR_VAL; + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * TCP callback function if a connection (opened by tcp_connect/do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ +static err_t +do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + + LWIP_UNUSED_ARG(pcb); + + conn = arg; + + if (conn == NULL) { + return ERR_VAL; + } + + conn->err = err; + if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + conn->state = NETCONN_NONE; + sys_sem_signal(conn->op_completed); + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void +do_connect(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.tcp == NULL) { + sys_sem_signal(msg->conn->op_completed); + return; + } + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + sys_sem_signal(msg->conn->op_completed); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + sys_sem_signal(msg->conn->op_completed); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->state = NETCONN_CONNECT; + setup_tcp(msg->conn); + msg->conn->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port, + do_connected); + /* sys_sem_signal() is called from do_connected (or err_tcp()), + * when the connection is established! */ + break; +#endif /* LWIP_TCP */ + default: + break; + } +} + +/** + * Connect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param msg the api_msg_msg pointing to the connection to disconnect + */ +void +do_disconnect(struct api_msg_msg *msg) +{ +#if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + udp_disconnect(msg->conn->pcb.udp); + } +#endif /* LWIP_UDP */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_listen(struct api_msg_msg *msg) +{ +#if LWIP_TCP + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { + if (msg->conn->pcb.tcp->state == CLOSED) { +#if TCP_LISTEN_BACKLOG + struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + if (lpcb == NULL) { + msg->conn->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (msg->conn->recvmbox != SYS_MBOX_NULL) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(msg->conn->recvmbox); + msg->conn->recvmbox = SYS_MBOX_NULL; + } + if (msg->conn->acceptmbox == SYS_MBOX_NULL) { + if ((msg->conn->acceptmbox = sys_mbox_new(DEFAULT_ACCEPTMBOX_SIZE)) == SYS_MBOX_NULL) { + msg->conn->err = ERR_MEM; + } + } + if (msg->conn->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } + } + } else { + msg->conn->err = ERR_CONN; + } + } + } + } +#endif /* LWIP_TCP */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_send(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.b->addr == NULL) { + msg->conn->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->conn->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, msg->msg.b->addr); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.b->addr == NULL) { + msg->conn->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->conn->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, msg->msg.b->addr, msg->msg.b->port); + } + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Recv some data from a RAW or UDP pcb contained in a netconn + * Called from netconn_recv + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_recv(struct api_msg_msg *msg) +{ +#if LWIP_TCP + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { +#if TCP_LISTEN_BACKLOG + if (msg->conn->pcb.tcp->state == LISTEN) { + tcp_accepted(msg->conn->pcb.tcp); + } else +#endif /* TCP_LISTEN_BACKLOG */ + { + tcp_recved(msg->conn->pcb.tcp, msg->msg.r.len); + } + } + } + } +#endif /* LWIP_TCP */ + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +do_writemore(struct netconn *conn) +{ + err_t err; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + + dataptr = (u8_t*)conn->write_msg->msg.w.dataptr + conn->write_offset; + if ((conn->write_msg->msg.w.len - conn->write_offset > 0xffff)) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } else { + len = conn->write_msg->msg.w.len - conn->write_offset; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } + + err = tcp_write(conn->pcb.tcp, dataptr, len, conn->write_msg->msg.w.apiflags); + LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->write_msg->msg.w.len)); + if (err == ERR_OK) { + conn->write_offset += len; + if (conn->write_offset == conn->write_msg->msg.w.len) { + /* everything was written */ + write_finished = 1; + conn->write_msg = NULL; + conn->write_offset = 0; + } + err = tcp_output_nagle(conn->pcb.tcp); + conn->err = err; + if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT)) { + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + } else if (err == ERR_MEM) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_enqueue returned ERR_MEM, try tcp_output anyway */ + err = tcp_output(conn->pcb.tcp); + +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + conn->err = err; + write_finished = 1; + } + + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if (conn->write_delayed != 0) +#endif + { + sys_sem_signal(conn->op_completed); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else + return ERR_MEM; +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_write(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { +#if LWIP_TCP + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by do_writemore */ + msg->conn->write_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->write_delayed = 0; + if (do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(msg->conn->op_completed, 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else + do_writemore(msg->conn); +#endif + /* for both cases: if do_writemore was called, don't ACK the APIMSG! */ + return; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->conn->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + *(msg->msg.ad.ipaddr) = (msg->msg.ad.local?msg->conn->pcb.ip->local_ip:msg->conn->pcb.ip->remote_ip); + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->conn->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->conn->err = ERR_CONN; + } else { + *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port); + break; +#endif /* LWIP_TCP */ + } + } else { + msg->conn->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close a TCP pcb contained in a netconn + * Called from netconn_close + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_close(struct api_msg_msg *msg) +{ +#if LWIP_TCP + if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + msg->conn->state = NETCONN_CLOSE; + do_close_internal(msg->conn); + /* for tcp netconns, do_close_internal ACKs the message */ + } else +#endif /* LWIP_TCP */ + { + msg->conn->err = ERR_VAL; + TCPIP_APIMSG_ACK(msg); + } +} + +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_join_leave_group(struct api_msg_msg *msg) +{ + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { +#if LWIP_UDP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->conn->err = igmp_joingroup(msg->msg.jl.interface, msg->msg.jl.multiaddr); + } else { + msg->conn->err = igmp_leavegroup(msg->msg.jl.interface, msg->msg.jl.multiaddr); + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->conn->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ +static void +do_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0); + + if (ipaddr == NULL) { + /* timeout or memory error */ + *msg->err = ERR_VAL; + } else { + /* address was resolved */ + *msg->err = ERR_OK; + *msg->addr = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg); + if (*msg->err != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/lab/net/lwip/api/err.c b/lab/net/lwip/api/err.c new file mode 100644 index 0000000..a90cb98 --- /dev/null +++ b/lab/net/lwip/api/err.c @@ -0,0 +1,74 @@ +/** + * @file + * Error Management module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/err.h" + +#ifdef LWIP_DEBUG + +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Connection aborted.", /* ERR_ABRT -5 */ + "Connection reset.", /* ERR_RST -6 */ + "Connection closed.", /* ERR_CLSD -7 */ + "Not connected.", /* ERR_CONN -8 */ + "Illegal value.", /* ERR_VAL -9 */ + "Illegal argument.", /* ERR_ARG -10 */ + "Address in use.", /* ERR_USE -11 */ + "Low-level netif error.", /* ERR_IF -12 */ + "Already connected.", /* ERR_ISCONN -13 */ + "Operation in progress." /* ERR_INPROGRESS -14 */ +}; + +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * +lwip_strerr(err_t err) +{ + return err_strerr[-err]; + +} + +#endif /* LWIP_DEBUG */ diff --git a/lab/net/lwip/api/netbuf.c b/lab/net/lwip/api/netbuf.c new file mode 100644 index 0000000..27120eb --- /dev/null +++ b/lab/net/lwip/api/netbuf.c @@ -0,0 +1,235 @@ +/** + * @file + * Network buffer management + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + buf->addr = NULL; + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * Allocate memory for a packet buffer for a given netbuf. + * + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * Free the packet buffer included in a netbuf + * + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_chain(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retreived, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/lab/net/lwip/api/netdb.c b/lab/net/lwip/api/netdb.c new file mode 100644 index 0000000..3185cfb --- /dev/null +++ b/lab/net/lwip/api/netdb.c @@ -0,0 +1,353 @@ +/** + * @file + * API functions for name resolving + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + struct ip_addr *addrs; + struct ip_addr addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + struct ip_addr addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE struct ip_addr s_hostent_addr; + HOSTENT_STORAGE struct ip_addr *s_phostent_addr; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr = &s_hostent_addr; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(struct ip_addr); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == 0x%08lX\n",(u32_t)(s_hostent.h_aliases))); + if (s_hostent.h_aliases != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_aliases[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == 0x%08lX\n", idx, s_hostent.h_aliases[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx])); + } + } + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %lu\n", (u32_t)(s_hostent.h_addrtype))); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %lu\n", (u32_t)(s_hostent.h_length))); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == 0x%08lX\n", s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == 0x%08lX\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, inet_ntoa(*((struct in_addr*)(s_hostent.h_addr_list[idx]))))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == 0)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &(h->addr)); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = ENSRNOTFOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addrs = &(h->addr); + h->aliases = NULL; + ret->h_name = (char*)hostname; + ret->h_aliases = &(h->aliases); + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(struct ip_addr); + ret->h_addr_list = (char**)&(h->addrs); + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + if (ai->ai_addr != NULL) { + mem_free(ai->ai_addr); + } + if (ai->ai_canonname != NULL) { + mem_free(ai->ai_canonname); + } + next = ai->ai_next; + mem_free(ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + struct ip_addr addr; + struct addrinfo *ai; + struct sockaddr_in *sa = NULL; + int port_nr = 0; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + err = netconn_gethostbyname(nodename, &addr); + if (err != ERR_OK) { + return EAI_FAIL; + } + } else { + /* service location specified, use loopback address */ + addr.addr = INADDR_LOOPBACK; + } + + ai = mem_malloc(sizeof(struct addrinfo)); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, sizeof(struct addrinfo)); + sa = mem_malloc(sizeof(struct sockaddr_in)); + if (sa == NULL) { + goto memerr; + } + memset(sa, 0, sizeof(struct sockaddr_in)); + /* set up sockaddr */ + sa->sin_addr.s_addr = addr.addr; + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons(port_nr); + + /* set up addrinfo */ + ai->ai_family = AF_INET; + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + size_t namelen = strlen(nodename); + LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); + ai->ai_canonname = mem_malloc((mem_size_t)(namelen + 1)); + if (ai->ai_canonname == NULL) { + goto memerr; + } + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +memerr: + if (ai != NULL) { + mem_free(ai); + } + if (sa != NULL) { + mem_free(sa); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/lab/net/lwip/api/netifapi.c b/lab/net/lwip/api/netifapi.c new file mode 100644 index 0000000..4918373 --- /dev/null +++ b/lab/net/lwip/api/netifapi.c @@ -0,0 +1,126 @@ +/** + * @file + * Network Interface Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/tcpip.h" + +/** + * Call netif_add() inside the tcpip_thread context. + */ +void +do_netifapi_netif_add( struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw, + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + msg->err = ERR_IF; + } else { + msg->err = ERR_OK; + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +void +do_netifapi_netif_common( struct netifapi_msg_msg *msg) +{ + if (msg->msg.common.errtfunc!=NULL) { + msg->err = + msg->msg.common.errtfunc(msg->netif); + } else { + msg->err = ERR_OK; + msg->msg.common.voidfunc(msg->netif); + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_add; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + msg.msg.msg.add.state = state; + msg.msg.msg.add.init = init; + msg.msg.msg.add.input = input; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common( struct netif *netif, + void (* voidfunc)(struct netif *netif), + err_t (* errtfunc)(struct netif *netif) ) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_common; + msg.msg.netif = netif; + msg.msg.msg.common.voidfunc = voidfunc; + msg.msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/lab/net/lwip/api/sockets.c b/lab/net/lwip/api/sockets.c new file mode 100644 index 0000000..9057489 --- /dev/null +++ b/lab/net/lwip/api/sockets.c @@ -0,0 +1,1924 @@ +/** + * @file + * Sockets BSD-Like API module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * Improved by Marc Boucher and David Haas + * + */ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" + +#include + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** Contains all internal pointers and states used for a socket */ +struct lwip_socket { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + struct netbuf *lastdata; + /** offset in the data that was left from the previous read */ + u16_t lastoffset; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + u16_t rcvevent; + /** number of times data was received, set by event_callback(), + tested by select */ + u16_t sendevent; + /** socket flags (currently, only used for O_NONBLOCK) */ + u16_t flags; + /** last error that occurred on this socket */ + int err; +}; + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + sys_sem_t sem; +}; + +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket struct for which to change options */ + struct lwip_socket *sock; + /** socket index for which to change options */ + int s; + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ + void *optval; + /** size of *optval */ + socklen_t *optlen; + /** if an error occures, it is temporarily stored here */ + err_t err; +}; + +/** The global array of available sockets */ +static struct lwip_socket sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; + +/** Semaphore protecting the sockets array */ +static sys_sem_t socksem; +/** Semaphore protecting select_cb_list */ +static sys_sem_t selectsem; + +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + ECONNABORTED, /* ERR_ABRT -5 Connection aborted. */ + ECONNRESET, /* ERR_RST -6 Connection reset. */ + ESHUTDOWN, /* ERR_CLSD -7 Connection closed. */ + ENOTCONN, /* ERR_CONN -8 Not connected. */ + EINVAL, /* ERR_VAL -9 Illegal value. */ + EIO, /* ERR_ARG -10 Illegal argument. */ + EADDRINUSE, /* ERR_USE -11 Address in use. */ + -1, /* ERR_IF -12 Low-level netif error */ + -1, /* ERR_ISCONN -13 Already connected. */ + EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ +}; + +#define ERR_TO_ERRNO_TABLE_SIZE \ + (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) + +#define err_to_errno(err) \ + ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ + err_to_errno_table[-(err)] : EIO) + +#ifdef ERRNO +#define set_errno(err) errno = (err) +#else +#define set_errno(err) +#endif + +#define sock_set_errno(sk, e) do { \ + sk->err = (e); \ + set_errno(sk->err); \ +} while (0) + +/* Forward delcaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +static void lwip_getsockopt_internal(void *arg); +static void lwip_setsockopt_internal(void *arg); + +/** + * Initialize this module. This function has to be called before any other + * functions in this module! + */ +void +lwip_socket_init(void) +{ + socksem = sys_sem_new(1); + selectsem = sys_sem_new(1); +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_socket for the socket or NULL if not found + */ +static struct lwip_socket * +get_socket(int s) +{ + struct lwip_socket *sock; + + if ((s < 0) || (s >= NUM_SOCKETS)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); + set_errno(EBADF); + return NULL; + } + + sock = &sockets[s]; + + if (!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s)); + set_errno(EBADF); + return NULL; + } + + return sock; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn) +{ + int i; + + /* Protect socket array */ + sys_sem_wait(socksem); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + if (!sockets[i].conn) { + sockets[i].conn = newconn; + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + sockets[i].sendevent = 1; /* TCP send buf is empty */ + sockets[i].flags = 0; + sockets[i].err = 0; + sys_sem_signal(socksem); + return i; + } + } + sys_sem_signal(socksem); + return -1; +} + +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + +int +lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct lwip_socket *sock, *nsock; + struct netconn *newconn; + struct ip_addr naddr; + u16_t port; + int newsock; + struct sockaddr_in sin; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) + return -1; + + newconn = netconn_accept(sock->conn); + if (!newconn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err)); + sock_set_errno(sock, err_to_errno(sock->conn->err)); + return -1; + } + + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, &naddr, &port); + if (err != ERR_OK) { + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = naddr.addr; + + if (*addrlen > sizeof(sin)) + *addrlen = sizeof(sin); + + SMEMCPY(addr, &sin, *addrlen); + + newsock = alloc_socket(newconn); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); + newconn->callback = event_callback; + nsock = &sockets[newsock]; + LWIP_ASSERT("invalid socket pointer", nsock != NULL); + + sys_sem_wait(socksem); + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + nsock->rcvevent += -1 - newconn->socket; + newconn->socket = newsock; + sys_sem_signal(socksem); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); + //ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", port)); + + sock_set_errno(sock, 0); + return newsock; +} + +int +lwip_bind(int s, struct sockaddr *name, socklen_t namelen) +{ + struct lwip_socket *sock; + struct ip_addr local_addr; + u16_t local_port; + err_t err; + + sock = get_socket(s); + if (!sock) + return -1; + + LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((((struct sockaddr_in *)name)->sin_family) == AF_INET)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + local_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; + local_port = ((struct sockaddr_in *)name)->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + //ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(local_port))); + + err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_close(int s) +{ + struct lwip_socket *sock; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + netconn_delete(sock->conn); + + sys_sem_wait(socksem); + if (sock->lastdata) { + netbuf_delete(sock->lastdata); + } + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->conn = NULL; + sock_set_errno(sock, 0); + sys_sem_signal(socksem); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_socket *sock; + err_t err; + + sock = get_socket(s); + if (!sock) + return -1; + + LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((((struct sockaddr_in *)name)->sin_family) == AF_INET)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + if (((struct sockaddr_in *)name)->sin_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + struct ip_addr remote_addr; + u16_t remote_port; + + remote_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; + remote_port = ((struct sockaddr_in *)name)->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + //ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(remote_port))); + + err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); + } + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: need TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_socket *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + + sock = get_socket(s); + if (!sock) + return -1; + + /* limit the "backlog" parameter to fit in an u8_t */ + if (backlog < 0) { + backlog = 0; + } + if (backlog > 0xff) { + backlog = 0xff; + } + + err = netconn_listen_with_backlog(sock->conn, backlog); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_recvfrom(int s, void *mem, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + struct lwip_socket *sock; + struct netbuf *buf; + u16_t buflen, copylen, off = 0; + struct ip_addr *addr; + u16_t port; + u8_t done = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %d, 0x%x, ..)\n", s, mem, len, flags)); + sock = get_socket(s); + if (!sock) + return -1; + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", (void*)sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || (sock->flags & O_NONBLOCK)) && !sock->rcvevent) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + sock->lastdata = buf = netconn_recv(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv netbuf=%p\n", (void*)buf)); + + if (!buf) { + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL!\n", s)); + sock_set_errno(sock, (((sock->conn->pcb.ip!=NULL) && (sock->conn->err==ERR_OK))?ETIMEDOUT:err_to_errno(sock->conn->err))); + return 0; + } + } + + buflen = netbuf_len(buf); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%d len=%d off=%d sock->lastoffset=%d\n", buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = len; + } + + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + netbuf_copy_partial(buf, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + len -= copylen; + if ( (len <= 0) || (buf->p->flags & PBUF_FLAG_PUSH) || !sock->rcvevent) { + done = 1; + } + } else { + done = 1; + } + + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK)==0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((sock->conn->type == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", (void*)buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", (void*)buf)); + netbuf_delete(buf); + } + } else { + done = 1; + } + } while (!done); + + /* Check to see from where the data was.*/ + if (from && fromlen) { + struct sockaddr_in sin; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = (struct ip_addr*)&(sin.sin_addr.s_addr); + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = addr->addr; + + if (*fromlen > sizeof(sin)) + *fromlen = sizeof(sin); + + SMEMCPY(from, &sin, *fromlen); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + //ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, off)); + } else { +#if SOCKETS_DEBUG + struct sockaddr_in sin; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = (struct ip_addr*)&(sin.sin_addr.s_addr); + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + //ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, off)); +#endif /* SOCKETS_DEBUG */ + } + + sock_set_errno(sock, 0); + return off; +} + +int +lwip_read(int s, void *mem, int len) +{ + return lwip_recvfrom(s, mem, len, 0, NULL, NULL); +} + +int +lwip_recv(int s, void *mem, int len, unsigned int flags) +{ + return lwip_recvfrom(s, mem, len, flags, NULL, NULL); +} + +int +lwip_send(int s, const void *data, int size, unsigned int flags) +{ + struct lwip_socket *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%d, flags=0x%x)\n", + s, data, size, flags)); + + sock = get_socket(s); + if (!sock) + return -1; + + if (sock->conn->type!=NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + err = netconn_write(sock->conn, data, size, NETCONN_COPY | ((flags & MSG_MORE)?NETCONN_MORE:0)); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%d\n", s, err, size)); + sock_set_errno(sock, err_to_errno(err)); + return (err==ERR_OK?size:-1); +} + +int +lwip_sendto(int s, const void *data, int size, unsigned int flags, + struct sockaddr *to, socklen_t tolen) +{ + struct lwip_socket *sock; + struct ip_addr remote_addr; + int err; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; + u16_t remote_port; +#endif + + sock = get_socket(s); + if (!sock) + return -1; + + if (sock->conn->type==NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", + ((size >= 0) && (size <= 0xffff))); + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + ((tolen == sizeof(struct sockaddr_in)) && + ((((struct sockaddr_in *)to)->sin_family) == AF_INET))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + +#if LWIP_TCPIP_CORE_LOCKING + /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ + { struct pbuf* p; + + p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (p == NULL) { + err = ERR_MEM; + } else { + p->payload = (void*)data; + p->len = p->tot_len = size; + + remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; + + LOCK_TCPIP_CORE(); + if (sock->conn->type==NETCONN_RAW) { + err = sock->conn->err = raw_sendto(sock->conn->pcb.raw, p, &remote_addr); + } else { + err = sock->conn->err = udp_sendto(sock->conn->pcb.udp, p, &remote_addr, ntohs(((struct sockaddr_in *)to)->sin_port)); + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } + } +#else + /* initialize a buffer */ + buf.p = buf.ptr = NULL; + if (to) { + remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; + remote_port = ntohs(((struct sockaddr_in *)to)->sin_port); + buf.addr = &remote_addr; + buf.port = remote_port; + } else { + remote_addr.addr = 0; + remote_port = 0; + buf.addr = NULL; + buf.port = 0; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, size=%d, flags=0x%x to=", + s, data, size, flags)); + //ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", remote_port)); + + /* make the buffer point to the data that should be sent */ + if ((err = netbuf_ref(&buf, data, size)) == ERR_OK) { + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + if (buf.p != NULL) { + pbuf_free(buf.p); + } +#endif /* LWIP_TCPIP_CORE_LOCKING */ + sock_set_errno(sock, err_to_errno(err)); + return (err==ERR_OK?size:-1); +} + +int +lwip_socket(int domain, int type, int protocol) +{ + struct netconn *conn; + int i; + + LWIP_UNUSED_ARG(domain); + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_DGRAM: + conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? + NETCONN_UDPLITE : NETCONN_UDP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_STREAM: + conn = netconn_new_with_callback(NETCONN_TCP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); + set_errno(EINVAL); + return -1; + } + + if (!conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); + set_errno(ENOBUFS); + return -1; + } + + i = alloc_socket(conn); + + if (i == -1) { + netconn_delete(conn); + set_errno(ENFILE); + return -1; + } + conn->socket = i; + LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); + set_errno(0); + return i; +} + +int +lwip_write(int s, const void *data, int size) +{ + return lwip_send(s, data, size, 0); +} + +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * exceptset is not used for now!!! + * + * @param maxfdp1 the highest socket index in the sets + * @param readset in: set of sockets to check for read events; + * out: set of sockets that had read events + * @param writeset in: set of sockets to check for write events; + * out: set of sockets that had write events + * @param exceptset not yet implemented + * @return number of sockets that had events (read+write) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_socket *p_sock; + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for(i = 0; i < maxfdp1; i++) { + if (FD_ISSET(i, readset)) { + /* See if netconn of this socket is ready for read */ + p_sock = get_socket(i); + if (p_sock && (p_sock->lastdata || p_sock->rcvevent)) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + } + if (FD_ISSET(i, writeset)) { + /* See if netconn of this socket is ready for write */ + p_sock = get_socket(i); + if (p_sock && p_sock->sendevent) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + } + } + *readset = lreadset; + *writeset = lwriteset; + FD_ZERO(exceptset); + + return nready; +} + + +/** + * Processing exceptset is not yet implemented. + */ +int +lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + int i; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + struct lwip_select_cb *p_selcb; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%ld tvusec=%ld)\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? timeout->tv_sec : -1L, timeout ? timeout->tv_usec : -1L)); + + select_cb.next = 0; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; + + /* Protect ourselves searching through the list */ + sys_sem_wait(selectsem); + + if (readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + if (writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + if (exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); + + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + sys_sem_signal(selectsem); + if (readset) + FD_ZERO(readset); + if (writeset) + FD_ZERO(writeset); + if (exceptset) + FD_ZERO(exceptset); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + set_errno(0); + + return 0; + } + + /* add our semaphore to list */ + /* We don't actually need any dynamic memory. Our entry on the + * list is only valid while we are in this function, so it's ok + * to use local variables */ + + select_cb.sem = sys_sem_new(0); + /* Note that we are still protected */ + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + select_cb_list = &select_cb; + + /* Now we can safely unprotect */ + sys_sem_signal(selectsem); + + /* Now just wait to be woken */ + if (timeout == 0) + /* Wait forever */ + msectimeout = 0; + else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if(msectimeout == 0) + msectimeout = 1; + } + + i = sys_sem_wait_timeout(select_cb.sem, msectimeout); + + /* Take us off the list */ + sys_sem_wait(selectsem); + if (select_cb_list == &select_cb) + select_cb_list = select_cb.next; + else + for (p_selcb = select_cb_list; p_selcb; p_selcb = p_selcb->next) { + if (p_selcb->next == &select_cb) { + p_selcb->next = select_cb.next; + break; + } + } + + sys_sem_signal(selectsem); + + sys_sem_free(select_cb.sem); + if (i == 0) { + /* Timeout */ + if (readset) + FD_ZERO(readset); + if (writeset) + FD_ZERO(writeset); + if (exceptset) + FD_ZERO(exceptset); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + set_errno(0); + + return 0; + } + + if (readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + if (writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + if (exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + /* See what's set */ + nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); + } else + sys_sem_signal(selectsem); + + if (readset) + *readset = lreadset; + if (writeset) + *writeset = lwriteset; + if (exceptset) + *exceptset = lexceptset; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); + set_errno(0); + + return nready; +} + +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ +static void +event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + int s; + struct lwip_socket *sock; + struct lwip_select_cb *scb; + + LWIP_UNUSED_ARG(len); + + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + sys_sem_wait(socksem); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + sys_sem_signal(socksem); + return; + } + sys_sem_signal(socksem); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + sys_sem_wait(selectsem); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + sys_sem_signal(selectsem); + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code is written this way to protect the select link list + but to avoid a deadlock situation by releasing socksem before + signalling for the select. This means we need to go through the list + multiple times ONLY IF a select was actually waiting. We go through + the list the number of waiting select calls + 1. This list is + expected to be small. */ + while (1) { + sys_sem_wait(selectsem); + for (scb = select_cb_list; scb; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* Test this select call for our socket */ + if (scb->readset && FD_ISSET(s, scb->readset)) + if (sock->rcvevent) + break; + if (scb->writeset && FD_ISSET(s, scb->writeset)) + if (sock->sendevent) + break; + } + } + if (scb) { + scb->sem_signalled = 1; + sys_sem_signal(selectsem); + sys_sem_signal(scb->sem); + } else { + sys_sem_signal(selectsem); + break; + } + } +} + +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) +{ + LWIP_UNUSED_ARG(how); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + return lwip_close(s); /* XXX temporary hack until proper implementation */ +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_socket *sock; + struct sockaddr_in sin; + struct ip_addr naddr; + + sock = get_socket(s); + if (!sock) + return -1; + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + + /* get the IP address and port */ + netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + //ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%d)\n", sin.sin_port)); + + sin.sin_port = htons(sin.sin_port); + sin.sin_addr.s_addr = naddr.addr; + + if (*namelen > sizeof(sin)) + *namelen = sizeof(sin); + + SMEMCPY(name, &sin, *namelen); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} + +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + err_t err = ERR_OK; + struct lwip_socket *sock = get_socket(s); + struct lwip_setgetsockopt_data data; + + if (!sock) + return -1; + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_ERROR: + case SO_KEEPALIVE: + /* UNIMPL case SO_CONTIMEO: */ + /* UNIMPL case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + case SO_TYPE: + /* UNIMPL case SO_USELOOPBACK: */ + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; + + case SO_NO_CHECK: + if (*optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + break; + case IP_MULTICAST_IF: + if (*optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + break; +#endif /* LWIP_IGMP */ + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) + return 0; + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE*/ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + /* Now do the actual option processing */ + data.sock = sock; + data.level = level; + data.optname = optname; + data.optval = optval; + data.optlen = optlen; + data.err = err; + tcpip_callback(lwip_getsockopt_internal, &data); + sys_arch_sem_wait(sock->conn->op_completed, 0); + /* maybe lwip_getsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_getsockopt_internal(void *arg) +{ + struct lwip_socket *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /*case SO_USELOOPBACK: UNIMPL */ + *(int*)optval = sock->conn->pcb.ip->so_options & optname; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + case SO_TYPE: + switch (NETCONNTYPE_GROUP(sock->conn->type)) { + case NETCONN_RAW: + *(int*)optval = SOCK_RAW; + break; + case NETCONN_TCP: + *(int*)optval = SOCK_STREAM; + break; + case NETCONN_UDP: + *(int*)optval = SOCK_DGRAM; + break; + default: /* unrecognized socket type */ + *(int*)optval = sock->conn->type; + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (sock->conn->type) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + if (sock->err == 0) { + sock_set_errno(sock, err_to_errno(sock->conn->err)); + } + *(int *)optval = sock->err; + sock->err = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + *(int *)optval = sock->conn->recv_timeout; + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = sock->conn->recv_bufsize; + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_TOS: + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + *(u8_t*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + ((struct in_addr*) optval)->s_addr = sock->conn->pcb.udp->multicast_ip.addr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%x\n", + s, *(u32_t *)optval)); + break; +#endif /* LWIP_IGMP */ + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + *(int*)optval = (sock->conn->pcb.tcp->flags & TF_NODELAY); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + } /* switch (level) */ + sys_sem_signal(sock->conn->op_completed); +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + struct lwip_socket *sock = get_socket(s); + int err = ERR_OK; + struct lwip_setgetsockopt_data data; + + if (!sock) + return -1; + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case case SO_CONTIMEO: */ + /* UNIMPL case case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; + case SO_NO_CHECK: + if (optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_IF: + if (optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (optlen < sizeof(struct ip_mreq)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) + return 0; + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE */ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch (level) */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + + /* Now do the actual option processing */ + data.sock = sock; + data.level = level; + data.optname = optname; + data.optval = (void*)optval; + data.optlen = &optlen; + data.err = err; + tcpip_callback(lwip_setsockopt_internal, &data); + sys_arch_sem_wait(sock->conn->op_completed, 0); + /* maybe lwip_setsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_setsockopt_internal(void *arg) +{ + struct lwip_socket *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + const void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (*(int*)optval) { + sock->conn->pcb.ip->so_options |= optname; + } else { + sock->conn->pcb.ip->so_options &= ~optname; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + sock->conn->recv_timeout = ( *(int*)optval ); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + sock->conn->recv_bufsize = ( *(int*)optval ); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + if (*(int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %u\n", + s, sock->conn->pcb.ip->ttl)); + break; + case IP_TOS: + sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %u\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + sock->conn->pcb.udp->multicast_ip.addr = ((struct in_addr*) optval)->s_addr; + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + struct ip_mreq *imr = (struct ip_mreq *)optval; + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); + } else { + data->err = igmp_leavegroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + if (*(int*)optval) { + sock->conn->pcb.tcp->flags |= TF_NODELAY; + } else { + sock->conn->pcb.tcp->flags &= ~TF_NODELAY; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(int *)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %lu\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %lu\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %lu\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %lu\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(int*)optval != 0) && (*(int*)optval < 8)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = *(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(int*)optval != 0) && (*(int*)optval < 8)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = *(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + } /* switch (level) */ + sys_sem_signal(sock->conn->op_completed); +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_socket *sock = get_socket(s); + u16_t buflen = 0; + + if (!sock) + return -1; + + switch (cmd) { + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } + + SYS_ARCH_GET(sock->conn->recv_avail, *((u16_t*)argp)); + + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + buflen = netbuf_len(sock->lastdata); + buflen -= sock->lastoffset; + + *((u16_t*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %u\n", s, argp, *((u16_t*)argp))); + sock_set_errno(sock, 0); + return 0; + + case FIONBIO: + if (argp && *(u32_t*)argp) + sock->flags |= O_NONBLOCK; + else + sock->flags &= ~O_NONBLOCK; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags & O_NONBLOCK))); + sock_set_errno(sock, 0); + return 0; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; + } /* switch (cmd) */ +} + +#endif /* LWIP_SOCKET */ diff --git a/lab/net/lwip/api/tcpip.c b/lab/net/lwip/api/tcpip.c new file mode 100644 index 0000000..29384b6 --- /dev/null +++ b/lab/net/lwip/api/tcpip.c @@ -0,0 +1,559 @@ +/** + * @file + * Sequential API Main thread module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/ip_frag.h" +#include "lwip/tcp.h" +#include "lwip/autoip.h" +#include "lwip/dhcp.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* global variables */ +static void (* tcpip_init_done)(void *arg); +static void *tcpip_init_done_arg; +static sys_mbox_t mbox = SYS_MBOX_NULL; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_sem_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_TCP +/* global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +#if !NO_SYS +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} +#endif /* !NO_SYS */ +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ +static void +tcpip_thread(void *arg) +{ + struct tcpip_msg *msg; + LWIP_UNUSED_ARG(arg); + +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ + + if (tcpip_init_done != NULL) { + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + sys_mbox_fetch(mbox, (void *)&msg); + switch (msg->type) { +#if LWIP_NETCONN + case TCPIP_MSG_API: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.apimsg->function(&(msg->msg.apimsg->msg)); + break; +#endif /* LWIP_NETCONN */ + + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ARP + if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ARP */ + { ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; + +#if LWIP_NETIF_API + case TCPIP_MSG_NETIFAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg)); + msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg)); + break; +#endif /* LWIP_NETIF_API */ + + case TCPIP_MSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.f(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + + if(msg->msg.tmo.msecs != 0xffffffff) + sys_timeout (msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + else + sys_untimeout (msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + default: + break; + } + } +} + +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if netif doesn't got NETIF_FLAG_ETHARP flag) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; + } + return ERR_VAL; +} + +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param f the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_callback_with_block(void (*f)(void *ctx), void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.f = f; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(mbox, msg); + } else { + if (sys_mbox_trypost(mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; + } + return ERR_VAL; +} + +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_NETCONN +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_apimsg(struct api_msg *apimsg) +{ + struct tcpip_msg msg; + + if (mbox != SYS_MBOX_NULL) { + msg.type = TCPIP_MSG_API; + msg.msg.apimsg = apimsg; + sys_mbox_post(mbox, &msg); + sys_arch_sem_wait(apimsg->msg.conn->op_completed, 0); + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_CORE_LOCKING +/** + * Call the lower part of a netconn_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_apimsg()) + */ +err_t +tcpip_apimsg_lock(struct api_msg *apimsg) +{ + LOCK_TCPIP_CORE(); + apimsg->function(&(apimsg->msg)); + UNLOCK_TCPIP_CORE(); + return ERR_OK; + +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + +#if LWIP_NETIF_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a netifapi_* + * function. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_netifapi(struct netifapi_msg* netifapimsg) +{ + struct tcpip_msg msg; + + if (mbox != SYS_MBOX_NULL) { + netifapimsg->msg.sem = sys_sem_new(0); + if (netifapimsg->msg.sem == SYS_SEM_NULL) { + netifapimsg->msg.err = ERR_MEM; + return netifapimsg->msg.err; + } + + msg.type = TCPIP_MSG_NETIFAPI; + msg.msg.netifapimsg = netifapimsg; + sys_mbox_post(mbox, &msg); + sys_sem_wait(netifapimsg->msg.sem); + sys_sem_free(netifapimsg->msg.sem); + return netifapimsg->msg.err; + } + return ERR_VAL; +} +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a netifapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_netifapi()) + */ +err_t +tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) +{ + LOCK_TCPIP_CORE(); + netifapimsg->function(&(netifapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return netifapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +/** + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ +void +tcpip_init(void (* initfunc)(void *), void *arg) +{ + lwip_init(); + + tcpip_init_done = initfunc; + tcpip_init_done_arg = arg; + mbox = sys_mbox_new(TCPIP_MBOX_SIZE); +#if LWIP_TCPIP_CORE_LOCKING + lock_tcpip_core = sys_sem_new(1); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = p; + pbuf_free(q); +} + +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} + +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} + +#endif /* !NO_SYS */ diff --git a/lab/net/lwip/core/dhcp.c b/lab/net/lwip/core/dhcp.c new file mode 100644 index 0000000..8eb2783 --- /dev/null +++ b/lab/net/lwip/core/dhcp.c @@ -0,0 +1,1553 @@ +/** + * @file + * Dynamic Host Configuration Protocol client + * + */ + +/* + * + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * TODO: + * - Proper parsing of DHCP messages exploiting file/sname field overloading. + * - Add JavaDoc style documentation (API, internals). + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Please coordinate changes and requests with Leon Woestenberg + * + * + * Integration with your code: + * + * In lwip/dhcp.h + * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * Then have your application call dhcp_coarse_tmr() and + * dhcp_fine_tmr() on the defined intervals. + * + * dhcp_start(struct netif *netif); + * starts a DHCP client instance which configures the interface by + * obtaining an IP address lease and maintaining it. + * + * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif) + * to remove the DHCP client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "lwip/sys.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +/** global transaction identifier, must be + * unique for each DHCP request. We simply increment, starting + * with this value (easy to match with a packet analyzer) */ +static u32_t xid = 0xABCD0000; + +/* DHCP client state machine functions */ +static void dhcp_handle_ack(struct netif *netif); +static void dhcp_handle_nak(struct netif *netif); +static void dhcp_handle_offer(struct netif *netif); + +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_check(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ +static err_t dhcp_rebind(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/* receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static err_t dhcp_unfold_reply(struct dhcp *dhcp); +static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type); +static u8_t dhcp_get_option_byte(u8_t *ptr); +#if 0 +static u16_t dhcp_get_option_short(u8_t *ptr); +#endif +static u32_t dhcp_get_option_long(u8_t *ptr); +static void dhcp_free_reply(struct dhcp *dhcp); + +/* set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/* build outgoing messages */ +/* create a DHCP request, fill in common headers */ +static err_t dhcp_create_request(struct netif *netif); +/* free a DHCP request */ +static void dhcp_delete_request(struct netif *netif); +/* add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/* add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +/* always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_nak(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Set the interface down since the address must no longer be used, as per RFC2131 */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + /* Change to a defined state */ + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* We can immediately restart discovery */ + dhcp_discover(netif); +} + +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_check: could not perform ARP query\n")); + } + dhcp->tries++; + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); + dhcp_set_state(dhcp, DHCP_CHECKING); +} + +/** + * Remember the configuration offered by a DHCP server. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + /* obtain the server address */ + u8_t *option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SERVER_ID); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + if (option_ptr != NULL) { + dhcp->server_ip_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr)); + /* remember offered address */ + ip_addr_set(&dhcp->offered_ip_addr, (struct ip_addr *)&dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); + + dhcp_select(netif); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + +#if LWIP_NETIF_HOSTNAME + p = (const char*)netif->hostname; + if (p!=NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* TODO: we really should bind to a specific local interface here + but we cannot specify an unconfigured netif as it is addressless */ + /* send broadcast to any DHCP server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + /* reconnect to any (or to server here?!) */ + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); + dhcp_set_state(dhcp, DHCP_REQUESTING); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_select: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 4 ? dhcp->tries * 1000 : 4 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + * + */ +void +dhcp_coarse_tmr() +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and triggers (zeroes) now? */ + if (netif->dhcp->t2_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (netif->dhcp->t1_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + * + */ +void +dhcp_fine_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout > 1) { + netif->dhcp->request_timeout--; + } + else if (netif->dhcp->request_timeout == 1) { + netif->dhcp->request_timeout--; + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this clients' request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } + } + /* did not get response to renew request? */ + else if (dhcp->state == DHCP_RENEWING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + /* just retry renewal */ + /* note that the rebind timer will eventually time-out if renew does not work */ + dhcp_renew(netif); + /* did not get response to rebind request? */ + } else if (dhcp->state == DHCP_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t1_timeout(): must renew\n")); + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout(): must rebind\n")); + dhcp_rebind(netif); + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + u8_t *option_ptr; + /* clear options we might not get from the ACK */ + dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = 0; + dhcp->offered_bc_addr.addr = 0; + + /* lease time given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_LEASE_TIME); + if (option_ptr != NULL) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_long(option_ptr + 2); + } + /* renewal period given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T1); + if (option_ptr != NULL) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_long(option_ptr + 2); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T2); + if (option_ptr != NULL) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_long(option_ptr + 2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_set(&dhcp->offered_ip_addr, &dhcp->msg_in->yiaddr); + +/** + * Patch #1308 + * TODO: we must check if the file field is not overloaded by DHCP options! + */ +#if 0 + /* boot server address */ + ip_addr_set(&dhcp->offered_si_addr, &dhcp->msg_in->siaddr); + /* boot file name */ + if (dhcp->msg_in->file[0]) { + dhcp->boot_file_name = mem_malloc(strlen(dhcp->msg_in->file) + 1); + strcpy(dhcp->boot_file_name, dhcp->msg_in->file); + } +#endif + + /* subnet mask */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SUBNET_MASK); + /* subnet mask given? */ + if (option_ptr != NULL) { + dhcp->offered_sn_mask.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* gateway router */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_ROUTER); + if (option_ptr != NULL) { + dhcp->offered_gw_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* broadcast address */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_BROADCAST); + if (option_ptr != NULL) { + dhcp->offered_bc_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* DNS servers */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_DNS_SERVER); + if (option_ptr != NULL) { + u8_t n; + dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]) / (u32_t)sizeof(struct ip_addr); + /* limit to at most DHCP_MAX_DNS DNS servers */ + if (dhcp->dns_count > DHCP_MAX_DNS) + dhcp->dns_count = DHCP_MAX_DNS; + for (n = 0; n < dhcp->dns_count; n++) { + dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2 + n * 4])); +#if LWIP_DNS + dns_setserver( n, (struct ip_addr *)(&(dhcp->offered_dns_addr[n].addr))); +#endif /* LWIP_DNS */ + } +#if LWIP_DNS + dns_setserver( n, (struct ip_addr *)(&ip_addr_any)); +#endif /* LWIP_DNS */ + } +} + +/** + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + */ +err_t +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result = ERR_OK; + + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + dhcp = mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + /* store this dhcp client in the netif */ + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, ("dhcp_start(): restarting DHCP configuration\n")); + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* allocate UDP PCB */ + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + mem_free((void *)dhcp); + netif->dhcp = dhcp = NULL; + return ERR_MEM; + } + /* set up local and remote port for the pcb */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* set up the recv callback and argument */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + /* Set the flag that says this netif is handled by DHCP. */ + netif->flags |= NETIF_FLAG_DHCP; + return result; +} + +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + */ +void +dhcp_inform(struct netif *netif) +{ + struct dhcp *dhcp, *old_dhcp = netif->dhcp; + err_t result = ERR_OK; + dhcp = mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_inform(): could not allocate dhcp\n")); + return; + } + netif->dhcp = dhcp; + memset(dhcp, 0, sizeof(struct dhcp)); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): allocated dhcp\n")); + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_inform(): could not obtain pcb")); + mem_free((void *)dhcp); + return; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_INFORM); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + /* TODO: use netif->mtu ?! */ + dhcp_option_short(dhcp, 576); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + dhcp_delete_request(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_inform: could not allocate DHCP request\n")); + } + + if (dhcp != NULL) { + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + } + dhcp->pcb = NULL; + mem_free((void *)dhcp); + netif->dhcp = old_dhcp; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address. + * + * @param netif the network interface on which the reply was received + * @param addr The IP address we received a reply from + */ +void dhcp_arp_reply(struct netif *netif, struct ip_addr *addr) +{ + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", addr->addr)); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_DECLINE); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* @todo: should we really connect here? we are performing sendto() */ + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_decline: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif + + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_discover()\n")); + ip_addr_set(&dhcp->offered_ip_addr, IP_ADDR_ANY); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_DISCOVER); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); + dhcp_set_state(dhcp, DHCP_SELECTING); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_discover: could not allocate DHCP request\n")); + } + dhcp->tries++; +#if LWIP_DHCP_AUTOIP_COOP + /* that means we waited 57 seconds */ + if(dhcp->tries >= 9 && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = dhcp->tries < 4 ? (dhcp->tries + 1) * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + struct ip_addr sn_mask, gw_addr; + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + } + /* copy offered network mask */ + ip_addr_set(&sn_mask, &dhcp->offered_sn_mask); + + /* subnet mask not given? */ + /* TODO: this is not a valid check. what if the network mask is 0? */ + if (sn_mask.addr == 0) { + /* choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&sn_mask); + if (first_octet <= 127) { + sn_mask.addr = htonl(0xff000000); + } else if (first_octet >= 192) { + sn_mask.addr = htonl(0xffffff00); + } else { + sn_mask.addr = htonl(0xffff0000); + } + } + + ip_addr_set(&gw_addr, &dhcp->offered_gw_addr); + /* gateway address not given? */ + if (gw_addr.addr == 0) { + /* copy network address */ + gw_addr.addr = (dhcp->offered_ip_addr.addr & sn_mask.addr); + /* use first host address on network as gateway */ + gw_addr.addr |= htonl(0x00000001); + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", sn_mask.addr)); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", gw_addr.addr)); + netif_set_gw(netif, &gw_addr); + /* bring the interface up */ + netif_set_up(netif); + /* netif is now bound to DHCP leased address */ + dhcp_set_state(dhcp, DHCP_BOUND); +} + +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + /* TODO: use netif->mtu in some way */ + dhcp_option_short(dhcp, 576); + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); +#endif + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT); + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_request(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_renew: could not allocate DHCP request\n")); + } + dhcp->tries++; + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_rebind: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_release()\n")); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + dhcp->server_ip_addr.addr = 0; + dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->dns_count = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_RELEASE); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT); + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_release: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + /* bring the interface down */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + /* Remove the flag that says this netif is handled by DHCP. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) { + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + } + if (dhcp->p != NULL) { + pbuf_free(dhcp->p); + dhcp->p = NULL; + } + /* free unfolded reply */ + dhcp_free_reply(dhcp); + mem_free((void *)dhcp); + netif->dhcp = NULL; + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + * + * TODO: we might also want to reset the timeout here? + */ +static void +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void +dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} + +static void +dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); +} + +static void +dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); +} + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a conitguous piece of memory, and + * use that further on. + * + */ +static err_t +dhcp_unfold_reply(struct dhcp *dhcp) +{ + u16_t ret; + LWIP_ERROR("dhcp != NULL", (dhcp != NULL), return ERR_ARG;); + LWIP_ERROR("dhcp->p != NULL", (dhcp->p != NULL), return ERR_VAL;); + /* free any left-overs from previous unfolds */ + dhcp_free_reply(dhcp); + /* options present? */ + if (dhcp->p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)) { + dhcp->options_in_len = dhcp->p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + dhcp->options_in = mem_malloc(dhcp->options_in_len); + if (dhcp->options_in == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->options\n")); + return ERR_MEM; + } + } + dhcp->msg_in = mem_malloc(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + if (dhcp->msg_in == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n")); + mem_free((void *)dhcp->options_in); + dhcp->options_in = NULL; + return ERR_MEM; + } + + /** copy the DHCP message without options */ + ret = pbuf_copy_partial(dhcp->p, dhcp->msg_in, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN, 0); + LWIP_ASSERT("ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN", ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", + sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)); + + if (dhcp->options_in != NULL) { + /** copy the DHCP options */ + ret = pbuf_copy_partial(dhcp->p, dhcp->options_in, dhcp->options_in_len, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + LWIP_ASSERT("ret == dhcp->options_in_len", ret == dhcp->options_in_len); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", + dhcp->options_in_len)); + } + LWIP_UNUSED_ARG(ret); + return ERR_OK; +} + +/** + * Free the incoming DHCP message including contiguous copy of + * its DHCP options. + * + */ +static void dhcp_free_reply(struct dhcp *dhcp) +{ + if (dhcp->msg_in != NULL) { + mem_free((void *)dhcp->msg_in); + dhcp->msg_in = NULL; + } + if (dhcp->options_in) { + mem_free((void *)dhcp->options_in); + dhcp->options_in = NULL; + dhcp->options_in_len = 0; + } + LWIP_DEBUGF(DHCP_DEBUG, ("dhcp_free_reply(): free'd\n")); +} + + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp = netif->dhcp; + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t *options_ptr; + u8_t msg_type; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + (u16_t)(ntohl(addr->addr) >> 24 & 0xff), (u16_t)(ntohl(addr->addr) >> 16 & 0xff), + (u16_t)(ntohl(addr->addr) >> 8 & 0xff), (u16_t)(ntohl(addr->addr) & 0xff), port)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + dhcp->p = p; + /* TODO: check packet length before reading them */ + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + goto free_pbuf_and_return; + } + } + /* match transaction ID against what we expected */ + if (ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; + } + /* option fields could be unfold? */ + if (dhcp_unfold_reply(dhcp) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + options_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_MESSAGE_TYPE); + if (options_ptr == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; + } + + /* read DHCP message type */ + msg_type = dhcp_get_option_byte(options_ptr + 2); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_REQUESTING) { + dhcp_handle_ack(netif); + dhcp->request_timeout = 0; +#if DHCP_DOES_ARP_CHECK + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) { + dhcp->request_timeout = 0; + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || + (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("DHCP_NAK received\n")); + dhcp->request_timeout = 0; + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } +free_pbuf_and_return: + pbuf_free(p); + dhcp->p = NULL; +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_create_request(struct netif *netif) +{ + struct dhcp *dhcp; + u16_t i; + LWIP_ERROR("dhcp_create_request: netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_create_request: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_request: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_request: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("dhcp_create_request(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_request: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* give unique transaction identifier to this request */ + dhcp->xid = xid++; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("transaction id xid++(%"X32_F") dhcp->xid(%"U32_F")\n",xid,dhcp->xid)); + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* TODO: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + /* TODO: make link layer independent */ + dhcp->msg_out->hlen = DHCP_HLEN_ETH; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + dhcp->msg_out->flags = 0; + dhcp->msg_out->ciaddr.addr = netif->ip_addr.addr; + dhcp->msg_out->yiaddr.addr = 0; + dhcp->msg_out->siaddr.addr = 0; + dhcp->msg_out->giaddr.addr = 0; + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } + dhcp->msg_out->cookie = htonl(0x63825363UL); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_delete_request(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_delete_request: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_delete_request: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_request: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_request: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure + */ +static void +dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) { + /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */ + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +/** + * Find the offset of a DHCP option inside the DHCP message. + * + * @param dhcp DHCP client + * @param option_type + * + * @return a byte offset into the UDP message where the option was found, or + * zero if the given option was not found. + */ +static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type) +{ + u8_t overload = DHCP_OVERLOAD_NONE; + + /* options available? */ + if ((dhcp->options_in != NULL) && (dhcp->options_in_len > 0)) { + /* start with options field */ + u8_t *options = (u8_t *)dhcp->options_in; + u16_t offset = 0; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while ((offset < dhcp->options_in_len) && (options[offset] != DHCP_OPTION_END)) { + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + /* are the sname and/or file field overloaded with options? */ + if (options[offset] == DHCP_OPTION_OVERLOAD) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 2, ("overloaded message detected\n")); + /* skip option type and length */ + offset += 2; + overload = options[offset++]; + } + /* requested option found */ + else if (options[offset] == option_type) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset)); + return &options[offset]; + /* skip option */ + } else { + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", options[offset])); + /* skip option type */ + offset++; + /* skip option length, and then length bytes */ + offset += 1 + options[offset]; + } + } + /* is this an overloaded message? */ + if (overload != DHCP_OVERLOAD_NONE) { + u16_t field_len; + if (overload == DHCP_OVERLOAD_FILE) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("overloaded file field\n")); + options = (u8_t *)&dhcp->msg_in->file; + field_len = DHCP_FILE_LEN; + } else if (overload == DHCP_OVERLOAD_SNAME) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("overloaded sname field\n")); + options = (u8_t *)&dhcp->msg_in->sname; + field_len = DHCP_SNAME_LEN; + /* TODO: check if else if () is necessary */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 1, ("overloaded sname and file field\n")); + options = (u8_t *)&dhcp->msg_in->sname; + field_len = DHCP_FILE_LEN + DHCP_SNAME_LEN; + } + offset = 0; + + /* at least 1 byte to read and no end marker */ + while ((offset < field_len) && (options[offset] != DHCP_OPTION_END)) { + if (options[offset] == option_type) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset=%"U16_F"\n", offset)); + return &options[offset]; + /* skip option */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("skipping option %"U16_F"\n", options[offset])); + /* skip option type */ + offset++; + offset += 1 + options[offset]; + } + } + } + } + return NULL; +} + +/** + * Return the byte of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u8_t +dhcp_get_option_byte(u8_t *ptr) +{ + LWIP_DEBUGF(DHCP_DEBUG, ("option byte value=%"U16_F"\n", (u16_t)(*ptr))); + return *ptr; +} + +#if 0 /* currently unused */ +/** + * Return the 16-bit value of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u16_t +dhcp_get_option_short(u8_t *ptr) +{ + u16_t value; + value = *ptr++ << 8; + value |= *ptr; + LWIP_DEBUGF(DHCP_DEBUG, ("option short value=%"U16_F"\n", value)); + return value; +} +#endif + +/** + * Return the 32-bit value of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u32_t dhcp_get_option_long(u8_t *ptr) +{ + u32_t value; + value = (u32_t)(*ptr++) << 24; + value |= (u32_t)(*ptr++) << 16; + value |= (u32_t)(*ptr++) << 8; + value |= (u32_t)(*ptr++); + LWIP_DEBUGF(DHCP_DEBUG, ("option long value=%"U32_F"\n", value)); + return value; +} + +#endif /* LWIP_DHCP */ diff --git a/lab/net/lwip/core/dns.c b/lab/net/lwip/core/dns.c new file mode 100644 index 0000000..3ba88db --- /dev/null +++ b/lab/net/lwip/core/dns.c @@ -0,0 +1,814 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + */ + +/** + + * This file implements a DNS host name to IP address resolver. + + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DNS.C + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/dns.h" + +#include + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS inet_addr("208.67.222.222") /* resolver1.opendns.com */ +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + u16_t id; + u8_t flags1; + u8_t flags2; + u16_t numquestions; + u16_t numanswers; + u16_t numauthrr; + u16_t numextrarr; +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS query message structure */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t class; +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS answer message structure */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t class; + u32_t ttl; + u16_t len; +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** DNS table entry */ +struct dns_table_entry { + u8_t state; + u8_t numdns; + u8_t tmr; + u8_t retries; + u8_t seqno; + u8_t err; + u32_t ttl; + char name[DNS_MAX_NAME_LENGTH]; + struct ip_addr ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static void dns_check_entries(void); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcb; +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static struct ip_addr dns_servers[DNS_MAX_SERVERS]; + +#if (DNS_USES_STATIC_BUF == 1) +static u8_t dns_payload[DNS_MSG_SIZE]; +#endif /* (DNS_USES_STATIC_BUF == 1) */ + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void +dns_init() +{ + struct ip_addr dnsserver; + + /* initialize default DNS server address */ + dnsserver.addr = DNS_SERVER_ADDRESS; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ + if (dns_pcb == NULL) { + dns_pcb = udp_new(); + + if (dns_pcb != NULL) { + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); + udp_recv(dns_pcb, dns_recv, NULL); + + /* initialize default DNS primary server */ + dns_setserver(0, &dnsserver); + } + } +} + +/** + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void +dns_setserver(u8_t numdns, struct ip_addr *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && (dnsserver->addr !=0 )) { + dns_servers[numdns] = (*dnsserver); + } +} + +/** + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +struct ip_addr +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return dns_servers[numdns]; + } else { + return *IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void +dns_tmr(void) +{ + if (dns_pcb != NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); + } +} + +/** + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @return the hostname's IP address, as u32_t (instead of struct ip_addr to + * better check for failure: != 0) or 0 if the hostname was not found + * in the cached dns_table. + */ +static u32_t +dns_lookup(const char *name) +{ + u8_t i; + + /* Walk through name list, return entry if found. If not, return NULL. */ + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (strcmp(name, dns_table[i].name) == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + return dns_table[i].ipaddr.addr; + } + } + + return 0; +} + +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t +dns_compare_name(unsigned char *query, unsigned char *response) +{ + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param query encoded DNS name in the DNS server response + * @return end of the name + */ +static unsigned char * +dns_parse_name(unsigned char *query) +{ + unsigned char n; + + do { + n = *query++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query; + --n; + }; + } + } while (*query != 0); + + return query + 1; +} + +/** + * Send a DNS query packet. + * + * @param numdns index of the DNS server in the dns_servers table + * @param name hostname to query + * @param id index of the hostname in dns_table, used as transaction ID in the + * DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t +dns_send(u8_t numdns, const char* name, u8_t id) +{ + err_t err; + struct dns_hdr *hdr; + struct dns_query qry; + struct pbuf *p; + char *query, *nptr; + const char *pHostname; + u8_t n; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(numdns), name)); + LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", dns_servers[numdns].addr != 0); + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dns_hdr) + DNS_MAX_NAME_LENGTH + + sizeof(struct dns_query), PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct dns_hdr*)p->payload; + memset(hdr, 0, sizeof(struct dns_hdr)); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RD; + hdr->numquestions = htons(1); + query = (char*)hdr + sizeof(struct dns_hdr); + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while(*pHostname != 0); + *query++='\0'; + + /* fill dns query */ + qry.type = htons(DNS_RRTYPE_A); + qry.class = htons(DNS_RRCLASS_IN); + MEMCPY( query, &qry, sizeof(struct dns_query)); + + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (query + sizeof(struct dns_query)) - ((char*)(p->payload))); + + /* connect to the server for faster receiving */ + udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); + /* send dns packet */ + err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void +dns_check_entry(u8_t i) +{ + struct dns_table_entry *pEntry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch(pEntry->state) { + + case DNS_STATE_NEW: { + /* initialize new entry */ + pEntry->state = DNS_STATE_ASKING; + pEntry->numdns = 0; + pEntry->tmr = 1; + pEntry->retries = 0; + + /* send DNS packet for this entry */ + dns_send(pEntry->numdns, pEntry->name, i); + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1].addr!=0)) { + /* change of server */ + pEntry->numdns++; + pEntry->tmr = 1; + pEntry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); + /* call specified callback function if provided */ + if (pEntry->found) + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + break; + } + } + + /* wait longer for the next retry */ + pEntry->tmr = pEntry->retries; + + /* send DNS packet for this entry */ + dns_send(pEntry->numdns, pEntry->name, i); + } + break; + } + + case DNS_STATE_DONE: { + /* if the time to live is nul */ + if (--pEntry->ttl == 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + } + break; + } + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + u8_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u8_t nquestions, nanswers; +#if (DNS_USES_STATIC_BUF == 0) + u8_t dns_payload[DNS_MSG_SIZE]; +#endif /* (DNS_USES_STATIC_BUF == 0) */ +#if (DNS_USES_STATIC_BUF == 2) + u8_t* dns_payload; +#endif /* (DNS_USES_STATIC_BUF == 2) */ + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr1; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (sizeof(struct dns_hdr) + sizeof(struct dns_query) + sizeof(struct dns_answer))) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr1; + } + +#if (DNS_USES_STATIC_BUF == 2) + dns_payload = mem_malloc(p->tot_len); + if (dns_payload == NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n")); + /* free pbuf and return */ + goto memerr1; + } +#endif /* (DNS_USES_STATIC_BUF == 2) */ + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct dns_hdr*)dns_payload; + i = htons(hdr->id); + if (i < DNS_TABLE_SIZE) { + pEntry = &dns_table[i]; + if(pEntry->state == DNS_STATE_ASKING) { + /* This entry is now completed. */ + pEntry->state = DNS_STATE_DONE; + pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = htons(hdr->numquestions); + nanswers = htons(hdr->numanswers); + + /* Check for error. If so, call callback to inform. */ + if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + +#if DNS_DOES_NAME_CHECK + /* Check if the name in the "question" part match with the name in the entry. */ + if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + sizeof(struct dns_hdr)) != 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } +#endif /* DNS_DOES_NAME_CHECK */ + + /* Skip the name in the "question" part */ + pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + sizeof(struct dns_hdr)) + sizeof(struct dns_query); + + while(nanswers > 0) { + /* skip answer resource record's host name */ + pHostname = (char *) dns_parse_name((unsigned char *)pHostname); + + /* Check for IP address type and Internet class. Others are discarded. */ + MEMCPY(&ans, pHostname, sizeof(struct dns_answer)); + if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) { + /* read the answer resource record's TTL, and maximize it if needed */ + pEntry->ttl = ntohl(ans.ttl); + if (pEntry->ttl > DNS_MAX_TTL) { + pEntry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + MEMCPY( &(pEntry->ipaddr), (pHostname+sizeof(struct dns_answer)), sizeof(struct ip_addr)); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); + } + /* deallocate memory and return */ + goto memerr2; + } else { + pHostname = pHostname + sizeof(struct dns_answer) + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + } + } + + /* deallocate memory and return */ + goto memerr2; + +responseerr: + /* ERROR: call specified callback function with NULL as name to indicate an error */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + } + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + +memerr2: +#if (DNS_USES_STATIC_BUF == 2) + /* free dns buffer */ + mem_free(dns_payload); +#endif /* (DNS_USES_STATIC_BUF == 2) */ + +memerr1: + /* free pbuf */ + pbuf_free(p); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param found a callback founction to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return @return a err_t return code. + */ +static err_t +dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *pEntry = NULL; + + /* search an unused entry, or the oldest one */ + lseq = lseqi = 0; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + pEntry = &dns_table[i]; + /* is it an unused entry ? */ + if (pEntry->state == DNS_STATE_UNUSED) + break; + + /* check if this is the oldest completed entry */ + if (pEntry->state == DNS_STATE_DONE) { + if ((dns_seqno - pEntry->seqno) > lseq) { + lseq = dns_seqno - pEntry->seqno; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can't be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + pEntry = &dns_table[i]; + } + } + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + pEntry->state = DNS_STATE_NEW; + pEntry->seqno = dns_seqno++; + pEntry->found = found; + pEntry->arg = callback_arg; + strcpy(pEntry->name, name); + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a struct ip_addr where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t +dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found, + void *callback_arg) +{ + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((dns_pcb == NULL) || (addr == NULL) || + (!hostname) || (!hostname[0]) || + (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { + return ERR_VAL; + } + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname,"localhost")==0) { + addr->addr = INADDR_LOOPBACK; + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK + * already have this address cached? */ + if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) || + ((addr->addr = dns_lookup(hostname)) != 0)) { + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/lab/net/lwip/core/init.c b/lab/net/lwip/core/init.c new file mode 100644 index 0000000..5e0b522 --- /dev/null +++ b/lab/net/lwip/core/init.c @@ -0,0 +1,253 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!LWIP_ARP && ARP_QUEUEING) + #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) + #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if (PPP_SUPPORT && (NO_SYS==1)) + #error "If you want to use PPP, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if ((NO_SYS==0) && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif +#if SO_REUSE +/* I removed the lot since this was an ugly hack. It broke the raw-API. + It also came with many ugly goto's, Christiaan Simons. */ + #error "SO_REUSE currently unavailable, this was a hack" +#endif + +#ifdef LWIP_DEBUG +static void +lwip_sanity_check(void) +{ + /* Warnings */ +#if LWIP_NETCONN + if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n")); +#endif /* LWIP_NETCONN */ +#if LWIP_TCP + if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n")); + if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS))) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n")); + if (TCP_SNDLOWAT > TCP_SND_BUF) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than or equal to TCP_SND_BUF.\n")); + if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n")); + if (TCP_WND < TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n")); +#endif /* LWIP_TCP */ +} +#else /* LWIP_DEBUG */ +#define lwip_sanity_check() +#endif /* LWIP_DEBUG */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Sanity check user-configurable values */ + lwip_sanity_check(); + + /* Modules initialization */ + stats_init(); + sys_init(); + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); +#if LWIP_ARP + etharp_init(); +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); +#endif /* LWIP_TCP */ +#if LWIP_AUTOIP + autoip_init(); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); +#endif /* LWIP_DNS */ +} diff --git a/lab/net/lwip/core/ipv4/autoip.c b/lab/net/lwip/core/ipv4/autoip.c new file mode 100644 index 0000000..aba450c --- /dev/null +++ b/lab/net/lwip/core/ipv4/autoip.c @@ -0,0 +1,432 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates random LL IP-Address for a network interface */ +static void autoip_create_rand_addr(struct netif *netif, struct ip_addr *RandomIPAddr); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/** + * Initialize this module + */ +void +autoip_init(void) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | 3, ("autoip_init()\n")); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if(defend) { + if(netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_start(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_start(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param RandomIPAddr ip address to initialize + */ +static void +autoip_create_rand_addr(struct netif *netif, struct ip_addr *RandomIPAddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities + */ + + RandomIPAddr->addr = (0xA9FE0100 + ((u32_t)(((u8_t)(netif->hwaddr[4])) | + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)) + netif->autoip->tried_llipaddr); + + if (RandomIPAddr->addr>0xA9FEFEFF) { + RandomIPAddr->addr = (0xA9FE0100 + (RandomIPAddr->addr-0xA9FEFEFF)); + } + if (RandomIPAddr->addr<0xA9FE0100) { + RandomIPAddr->addr = (0xA9FEFEFF - (0xA9FE0100-RandomIPAddr->addr)); + } + RandomIPAddr->addr = htonl(RandomIPAddr->addr); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, + ("autoip_create_rand_addr(): tried_llipaddr=%"U16_F", 0x%08"X32_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), (u32_t)(RandomIPAddr->addr))); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + struct ip_addr sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | 3, + ("autoip_bind(netif=%p) %c%c%"U16_F" 0x%08"X32_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, autoip->llipaddr.addr)); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if(netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + netif->ip_addr.addr = 0; + netif->netmask.addr = 0; + netif->gw.addr = 0; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if(autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = mem_malloc(sizeof(struct autoip)); + if(autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset( autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + memset(&autoip->llipaddr, 0, sizeof(struct ip_addr)); + autoip->lastconflict = 0; + } + + autoip_create_rand_addr(netif, &(autoip->llipaddr)); + autoip->tried_llipaddr++; + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + + if(autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + + return result; +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if(netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num == PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + } else { + etharp_request(netif, &(netif->autoip->llipaddr)); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | 3, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it + */ + autoip_bind(netif); + } + + if(netif->autoip->sent_num == ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | 3, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + netif->autoip->sent_num++; + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | 3, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + struct ip_addr sipaddr, dipaddr; + struct eth_addr netifaddr; + netifaddr.addr[0] = netif->hwaddr[0]; + netifaddr.addr[1] = netif->hwaddr[1]; + netifaddr.addr[2] = netif->hwaddr[2]; + netifaddr.addr[3] = netif->hwaddr[3]; + netifaddr.addr[4] = netif->hwaddr[4]; + netifaddr.addr[5] = netif->hwaddr[5]; + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); + SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_start(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 1, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/lab/net/lwip/core/ipv4/icmp.c b/lab/net/lwip/core/ipv4/icmp.c new file mode 100644 index 0000000..6a72c78 --- /dev/null +++ b/lab/net/lwip/core/ipv4/icmp.c @@ -0,0 +1,317 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the ip header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + struct ip_addr tmpaddr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + + iphdr = p->payload; + hlen = IPH_HL(iphdr) * 4; + if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ECHO: + /* broadcast or multicast destination address? */ + if (ip_addr_isbroadcast(&iphdr->dest, inp) || ip_addr_ismulticast(&iphdr->dest)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = p->payload; + tmpaddr.addr = iphdr->src.addr; + iphdr->src.addr = iphdr->dest.addr; + iphdr->dest.addr = tmpaddr.addr; + ICMPH_TYPE_SET(iecho, ICMP_ER); + /* adjust the checksum */ + if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) { + iecho->chksum += htons(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP_ECHO << 8); + } + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + ret = ip_output_if(p, &(iphdr->src), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_dur_hdr *idur; + + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = p->payload; + + idur = q->payload; + ICMPH_TYPE_SET(idur, ICMP_DUR); + ICMPH_CODE_SET(idur, t); + + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_dur_hdr), p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + idur->chksum = 0; + idur->chksum = inet_chksum(idur, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpoutdestunreachs(); + + ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_te_hdr *tehdr; + + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = p->payload; + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + tehdr = q->payload; + ICMPH_TYPE_SET(tehdr, ICMP_TE); + ICMPH_CODE_SET(tehdr, t); + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_dur_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + tehdr->chksum = 0; + tehdr->chksum = inet_chksum(tehdr, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* IP_FORWARD */ + +#endif /* LWIP_ICMP */ diff --git a/lab/net/lwip/core/ipv4/igmp.c b/lab/net/lwip/core/ipv4/igmp.c new file mode 100644 index 0000000..fb54cc9 --- /dev/null +++ b/lab/net/lwip/core/ipv4/igmp.c @@ -0,0 +1,808 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +static struct igmp_group* igmp_group_list; +static struct ip_addr allsystems; +static struct ip_addr allrouters; + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) group->interface)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %x\n", (int) netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif)); + netif->igmp_mac_filter( netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->interface == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups( struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %x\n", (int) netif)); + + while (group != NULL) { + if (group->interface == netif) { + igmp_delaying_member( group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, struct ip_addr *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, struct ip_addr *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->interface = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the ip header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest) +{ + struct ip_hdr * iphdr; + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + iphdr = p->payload; + if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the incoming IP address! */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && (igmp->igmp_group_address.addr == 0)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.v1_rxed); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } + + IGMP_STATS_INC(igmp.group_query_rxed); + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->interface == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member( groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (group->group_address.addr != 0) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + if (ip_addr_cmp (dest, &allsystems)) { + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-lookfor the group since we used dest last time */ + group = igmp_lookfor_group(inp, &igmp->igmp_group_address); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.unicast_query); + igmp_delaying_member( group, igmp->igmp_maxresp); + } + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + + IGMP_STATS_INC(igmp.report_rxed); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %x in state %x on group %x on if %x\n", (int) igmp->igmp_msgtype, (int) group->group_state, (int) &group, (int) group->interface)); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.join_sent); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.leave_sent); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer != 0) { + group->timer -= 1; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) group->interface)); + + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /** + * @todo Important !! this should be random 0 -> max_time. Find out how to do this + */ + group->timer = max_time; +} + +/** + * Stop a timer for an igmp_group + * + * @param group the igmp_group for which to stop the timer + */ +void +igmp_stop_timer(struct igmp_group *group) +{ + group->timer = 0; +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +void +igmp_delaying_member( struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && (maxresp > group->timer))) { + igmp_start_timer(group, (maxresp)/2); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +err_t +igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto, struct netif *netif) +{ + static u16_t ip_id = 0; + struct ip_hdr * iphdr = NULL; + u16_t * ra = NULL; + + /* First write in the "router alert" */ + if (pbuf_header(p, ROUTER_ALERTLEN)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n")); + return ERR_BUF; + } + + /* This is the "router alert" option */ + ra = p->payload; + ra[0] = htons (ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + + /* now the normal ip header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n")); + return ERR_BUF; + } + + iphdr = p->payload; + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /** @todo should be shared with ip.c - ip_output_if */ + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); + + ip_addr_set(&(iphdr->dest), dest); + + IPH_VHLTOS_SET(iphdr, 4, ((IP_HLEN + ROUTER_ALERTLEN) / 4), 0/*tos*/); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, (IP_HLEN + ROUTER_ALERTLEN))); +#endif + } else { + dest = &(iphdr->dest); + } + +#if IP_DEBUG + ip_debug_print(p); +#endif + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: sending to if %x\n", (int) netif)); + + return netif->output(netif, p, dest); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + struct ip_addr src = {0}; + struct ip_addr* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_set(&src, &((group->interface)->ip_addr)); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + IGMP_STATS_INC(igmp.report_sent); + ip_addr_set(&(igmp->igmp_group_address), &(group->group_address)); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_set(&(igmp->igmp_group_address), &(group->group_address)); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN); + + igmp_ip_output_if( p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface); + } + + pbuf_free (p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + } +} + +#endif /* LWIP_IGMP */ diff --git a/lab/net/lwip/core/ipv4/inet.c b/lab/net/lwip/core/ipv4/inet.c new file mode 100644 index 0000000..24c7ff4 --- /dev/null +++ b/lab/net/lwip/core/ipv4/inet.c @@ -0,0 +1,278 @@ +/** + * @file + * Functions common to all TCP/IPv4 modules, such as the byte order functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet.h" + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +inet_addr(const char *cp) +{ + struct in_addr val; + + if (inet_aton(cp, &val)) { + return (val.s_addr); + } + return (INADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +inet_aton(const char *cp, struct in_addr *addr) +{ + u32_t val; + int base, n, c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isprint(c) || !isspace(c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +inet_ntoa(struct in_addr addr) +{ + static char str[16]; + u32_t s_addr = addr.s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + + rp = str; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) + *rp++ = inv[i]; + *rp++ = '.'; + ap++; + } + *--rp = 0; + return str; +} + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +ntohs(u16_t n) +{ + return htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +ntohl(u32_t n) +{ + return htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/lab/net/lwip/core/ipv4/inet_chksum.c b/lab/net/lwip/core/ipv4/inet_chksum.c new file mode 100644 index 0000000..9248f7d --- /dev/null +++ b/lab/net/lwip/core/ipv4/inet_chksum.c @@ -0,0 +1,426 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/inet.h" + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 1 +# endif +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +/** Like the name says... */ +#define SWAP_BYTES_IN_WORD(w) ((w & 0xff) << 8) | ((w & 0xff00) >> 8) +/** Split an u32_t in two u16_ts and add them up */ +#define FOLD_U32T(u) ((u >> 16) + (u & 0x0000ffffUL)) + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +static u16_t +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +static u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((u32_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps;; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +static u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((u32_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((u32_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return sum; +} +#endif + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + u16_t chklen; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} diff --git a/lab/net/lwip/core/ipv4/ip.c b/lab/net/lwip/core/ipv4/ip.c new file mode 100644 index 0000000..f74f423 --- /dev/null +++ b/lab/net/lwip/core/ipv4/ip.c @@ -0,0 +1,624 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip_frag.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip_route(struct ip_addr *dest) +{ + struct netif *netif; + + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | 2, ("ip_route: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + return netif_default; +} + +#if IP_FORWARD +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + * @return the netif on which the packet was sent (NULL if it wasn't sent) + */ +static struct netif * +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + /* Find network interface where to forward this IP packet to. */ + netif = ip_route((struct ip_addr *)&(iphdr->dest)); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%"X32_F" found\n", + iphdr->dest.addr)); + snmp_inc_ipoutnoroutes(); + return (struct netif *)NULL; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + snmp_inc_ipoutnoroutes(); + return (struct netif *)NULL; + } + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return (struct netif *)NULL; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%"X32_F"\n", + iphdr->dest.addr)); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* transmit pbuf on chosen interface */ + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); + return netif; +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if LWIP_DHCP + int check_ip_src=1; +#endif /* LWIP_DHCP */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) + LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + if (iphdr_len > p->tot_len) + LWIP_DEBUGF(IP_DEBUG | 2, ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), " + "IP packet dropped.\n", + iphdr_len, p->tot_len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, iphdr_len); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(&(iphdr->dest))) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &(iphdr->dest)))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + iphdr->dest.addr, netif->ip_addr.addr, + iphdr->dest.addr & netif->netmask.addr, + netif->ip_addr.addr & netif->netmask.addr, + iphdr->dest.addr & ~(netif->netmask.addr))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(&(iphdr->dest), netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if LWIP_DHCP + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest))); + if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest) == DHCP_CLIENT_PORT) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* LWIP_DHCP */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if LWIP_DHCP + if (check_ip_src) +#endif /* LWIP_DHCP */ + { if ((ip_addr_isbroadcast(&(iphdr->src), inp)) || + (ip_addr_ismulticast(&(iphdr->src)))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p,inp,&(iphdr->dest)); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && + !ip_addr_ismulticast(&(iphdr->dest))) { + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); + } + } + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ + struct ip_hdr *iphdr; + static u16_t ip_id = 0; + + snmp_inc_ipoutrequests(); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); + + ip_addr_set(&(iphdr->dest), dest); + + IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif + } else { + /* IP header already included in p */ + iphdr = p->payload; + dest = &(iphdr->dest); + } + +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) + return ip_frag(p,netif,dest); +#endif + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +#if (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + + return netif_loop_output(netif, p, dest); + } else +#endif /* (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) */ + { + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + + return netif->output(netif, p, dest); + } +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + if ((netif = ip_route(dest)) == NULL) { + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = p->payload; + u8_t *payload; + + payload = (u8_t *)iphdr + IP_HLEN; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1(&iphdr->src), + ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), + ip4_addr4(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1(&iphdr->dest), + ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), + ip4_addr4(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/lab/net/lwip/core/ipv4/ip_addr.c b/lab/net/lwip/core/ipv4/ip_addr.c new file mode 100644 index 0000000..94bf467 --- /dev/null +++ b/lab/net/lwip/core/ipv4/ip_addr.c @@ -0,0 +1,84 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/inet.h" +#include "lwip/netif.h" + +#define IP_ADDR_ANY_VALUE 0x00000000UL +#define IP_ADDR_BROADCAST_VALUE 0xffffffffUL + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const struct ip_addr ip_addr_any = { IP_ADDR_ANY_VALUE }; +const struct ip_addr ip_addr_broadcast = { IP_ADDR_BROADCAST_VALUE }; + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t ip_addr_isbroadcast(struct ip_addr *addr, struct netif *netif) +{ + u32_t addr2test; + + addr2test = addr->addr; + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr2test == IP_ADDR_ANY_VALUE) || + (addr2test == IP_ADDR_ANY_VALUE)) + return 1; + /* no broadcast support on this network interface? */ + else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + else if (addr2test == netif->ip_addr.addr) + return 0; + /* on the same (sub) network... */ + else if (ip_addr_netcmp(addr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr2test & ~netif->netmask.addr) == + (IP_ADDR_BROADCAST_VALUE & ~netif->netmask.addr))) + /* => network broadcast address */ + return 1; + else + return 0; +} diff --git a/lab/net/lwip/core/ipv4/ip_frag.c b/lab/net/lwip/core/ipv4/ip_frag.c new file mode 100644 index 0000000..e06ba00 --- /dev/null +++ b/lab/net/lwip/core/ipv4/ip_frag.c @@ -0,0 +1,783 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + */ +struct ip_reass_helper { + struct pbuf *next_pbuf; + u16_t start; + u16_t end; +}; + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + int pbufs_freed = 0; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + pbufs_freed += pbuf_clen(p); + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + pbufs_freed += pbuf_clen(pcur); + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; + + IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU)]; +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else + struct pbuf *newpbuf; + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) + tmp = tmp | IP_MF; + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); + if (newpbuf == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf->payload = p->payload; + newpbuf->len = newpbuf->tot_len = newpbuflen; + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) + p = p->next; + } + poff = newpbuflen; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + +#if IP_FRAG_USES_STATIC_BUF + if (last) + pbuf_realloc(rambuf, left + IP_HLEN); + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); + return ERR_OK; +} +#endif /* IP_FRAG */ diff --git a/lab/net/lwip/core/ipv6/README b/lab/net/lwip/core/ipv6/README new file mode 100644 index 0000000..3620004 --- /dev/null +++ b/lab/net/lwip/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/lab/net/lwip/core/ipv6/icmp6.c b/lab/net/lwip/core/ipv6/icmp6.c new file mode 100644 index 0000000..4fcc895 --- /dev/null +++ b/lab/net/lwip/core/ipv6/icmp6.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" + +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + struct ip_addr tmpaddr; + + ICMP_STATS_INC(icmp.recv); + + /* TODO: check length before accessing payload! */ + + type = ((u8_t *)p->payload)[0]; + + switch (type) { + case ICMP6_ECHO: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + return; + } + iecho = p->payload; + iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN); + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); + ICMP_STATS_INC(icmp.chkerr); + /* return;*/ + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len)); + ip_addr_set(&tmpaddr, &(iphdr->src)); + ip_addr_set(&(iphdr->src), &(iphdr->dest)); + ip_addr_set(&(iphdr->dest), &tmpaddr); + iecho->type = ICMP6_ER; + /* adjust the checksum */ + if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) { + iecho->chksum += htons(ICMP6_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP6_ECHO << 8); + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); + ICMP_STATS_INC(icmp.xmit); + + /* LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ + ip_output_if (p, &(iphdr->src), IP_HDRINCL, + iphdr->hoplim, IP_PROTO_ICMP, inp); + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + + pbuf_free(p); +} + +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_dur_hdr *idur; + + /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + pbuf_free(p); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (8 + IP_HLEN + 8))); + + iphdr = p->payload; + + idur = q->payload; + idur->type = (u8_t)ICMP6_DUR; + idur->icode = (u8_t)t; + + SMEMCPY((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8); + + /* calculate checksum */ + idur->chksum = 0; + idur->chksum = inet_chksum(idur, q->len); + ICMP_STATS_INC(icmp.xmit); + + ip_output(q, NULL, + (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_te_hdr *tehdr; + + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n")); + + /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + pbuf_free(p); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (8 + IP_HLEN + 8))); + + iphdr = p->payload; + + tehdr = q->payload; + tehdr->type = (u8_t)ICMP6_TE; + tehdr->icode = (u8_t)t; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8); + + /* calculate checksum */ + tehdr->chksum = 0; + tehdr->chksum = inet_chksum(tehdr, q->len); + ICMP_STATS_INC(icmp.xmit); + ip_output(q, NULL, + (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/lab/net/lwip/core/ipv6/inet6.c b/lab/net/lwip/core/ipv6/inet6.c new file mode 100644 index 0000000..c3de85c --- /dev/null +++ b/lab/net/lwip/core/ipv6/inet6.c @@ -0,0 +1,163 @@ +/** + * @file + * Functions common to all TCP/IPv6 modules, such as the Internet checksum and the + * byte order functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/inet.h" + +/* chksum: + * + * Sums up all 16 bit words in a memory portion. Also includes any odd byte. + * This function is used by the other checksum functions. + * + * For now, this is not optimized. Must be optimized for the particular processor + * arcitecture on which it is to run. Preferebly coded in assembler. + */ + +static u32_t +chksum(void *dataptr, u16_t len) +{ + u16_t *sdataptr = dataptr; + u32_t acc; + + + for(acc = 0; len > 1; len -= 2) { + acc += *sdataptr++; + } + + /* add up any odd byte */ + if (len == 1) { + acc += htons((u16_t)(*(u8_t *)dataptr) << 8); + } + + return acc; + +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ + +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped, i; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + + for(i = 0; i < 8; i++) { + acc += ((u16_t *)src->addr)[i] & 0xffff; + acc += ((u16_t *)dest->addr)[i] & 0xffff; + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + } + acc += (u16_t)htons((u16_t)proto); + acc += ((u16_t *)&proto_len)[0] & 0xffff; + acc += ((u16_t *)&proto_len)[1] & 0xffff; + + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + u32_t acc, sum; + + acc = chksum(dataptr, len); + sum = (acc & 0xffff) + (acc >> 16); + sum += (sum >> 16); + return ~(sum & 0xffff); +} + +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + return ~(acc & 0xffff); +} diff --git a/lab/net/lwip/core/ipv6/ip6.c b/lab/net/lwip/core/ipv6/ip6.c new file mode 100644 index 0000000..ce5b501 --- /dev/null +++ b/lab/net/lwip/core/ipv6/ip6.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + + +/* ip.c + * + * This is the code for the IP layer for IPv6. + * + */ + + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" + +/* ip_init: + * + * Initializes the IP layer. + */ + +void +ip_init(void) +{ +} + +/* ip_route: + * + * Finds the appropriate network interface for a given IP address. It searches the + * list of network interfaces linearly. A match is found if the masked IP address of + * the network interface equals the masked IP address given to the function. + */ + +struct netif * +ip_route(struct ip_addr *dest) +{ + struct netif *netif; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + return netif; + } + } + + return netif_default; +} + +/* ip_forward: + * + * Forwards an IP packet. It finds an appropriate route for the packet, decrements + * the TTL value of the packet, adjusts the checksum and outputs the packet on the + * appropriate interface. + */ + +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr) +{ + struct netif *netif; + + PERF_START; + + if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { + + LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for ")); +#if IP_DEBUG + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); +#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP_DEBUG, ("\n")); + pbuf_free(p); + return; + } + /* Decrement TTL and send ICMP if ttl == 0. */ + if (--iphdr->hoplim == 0) { +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (iphdr->nexthdr != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + return; + } + + /* Incremental update of the IP checksum. */ + /* if (iphdr->chksum >= htons(0xffff - 0x100)) { + iphdr->chksum += htons(0x100) + 1; + } else { + iphdr->chksum += htons(0x100); + }*/ + + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to ")); +#if IP_DEBUG + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); +#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP_DEBUG, ("\n")); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + + PERF_STOP("ip_forward"); + + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); +} + +/* ip_input: + * + * This function is called by the network interface device driver when an IP packet is + * received. The function does the basic checks of the IP header such as packet size + * being at least larger than the header size etc. If the packet was not destined for + * us, the packet is forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + */ + +void +ip_input(struct pbuf *p, struct netif *inp) { + struct ip_hdr *iphdr; + struct netif *netif; + + + PERF_START; + +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + + IP_STATS_INC(ip.recv); + + /* identify the IP header */ + iphdr = p->payload; + + + if (iphdr->v != 6) { + LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n")); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + return; + } + + /* is this packet for us? */ + for(netif = netif_list; netif != NULL; netif = netif->next) { +#if IP_DEBUG + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest ")); + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr ")); + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("\n")); +#endif /* IP_DEBUG */ + if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) { + break; + } + } + + + if (netif == NULL) { + /* packet not for us, route or discard */ +#if IP_FORWARD + ip_forward(p, iphdr); +#endif + pbuf_free(p); + return; + } + + pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len)); + + /* send to upper layers */ +#if IP_DEBUG + /* LWIP_DEBUGF("ip_input: \n"); + ip_debug_print(p); + LWIP_DEBUGF("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ +#endif /* IP_DEBUG */ + + if(pbuf_header(p, -IP_HLEN)) { + LWIP_ASSERT("Can't move over header in packet", 0); + return; + } + + switch (iphdr->nexthdr) { + case IP_PROTO_UDP: + udp_input(p, inp); + break; + case IP_PROTO_TCP: + tcp_input(p, inp); + break; +#if LWIP_ICMP + case IP_PROTO_ICMP: + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable */ + icmp_dest_unreach(p, ICMP_DUR_PROTO); +#endif /* LWIP_ICMP */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n", + iphdr->nexthdr)); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + } + PERF_STOP("ip_input"); +} + + +/* ip_output_if: + * + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + */ + +err_t +ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, + u8_t proto, struct netif *netif) +{ + struct ip_hdr *iphdr; + + PERF_START; + + LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n")); + IP_STATS_INC(ip.err); + + return ERR_BUF; + } + LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); + + iphdr = p->payload; + + + if (dest != IP_HDRINCL) { + LWIP_DEBUGF(IP_DEBUG, ("!IP_HDRLINCL\n")); + iphdr->hoplim = ttl; + iphdr->nexthdr = proto; + iphdr->len = htons(p->tot_len - IP_HLEN); + ip_addr_set(&(iphdr->dest), dest); + + iphdr->v = 6; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + } else { + dest = &(iphdr->dest); + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len)); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + PERF_STOP("ip_output_if"); + return netif->output(netif, p, dest); +} + +/* ip_output: + * + * Simple interface to ip_output_if. It finds the outgoing network interface and + * calls upon ip_output_if to do the actual work. + */ + +err_t +ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto) +{ + struct netif *netif; + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if (p, src, dest, ttl, proto, netif); +} + +#if IP_DEBUG +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" | %"X16_F"%"X16_F" | %"X16_F"%"X16_F" | (v, traffic class, flow label)\n", + iphdr->v, + iphdr->tclass1, iphdr->tclass2, + iphdr->flow1, iphdr->flow2)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" | %2"U16_F" | %2"U16_F" | (len, nexthdr, hoplim)\n", + ntohs(iphdr->len), + iphdr->nexthdr, + iphdr->hoplim)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[0]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[0]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[1]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[1]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[2]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[2]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[3]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[3]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[0]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[0]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[1]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[1]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[2]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[2]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[3]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[3]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/lab/net/lwip/core/ipv6/ip6_addr.c b/lab/net/lwip/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..2da6cea --- /dev/null +++ b/lab/net/lwip/core/ipv6/ip6_addr.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/inet.h" + +u8_t +ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, + struct ip_addr *mask) +{ + return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) && + (addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) && + (addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) && + (addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3])); + +} + +u8_t +ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2) +{ + return(addr1->addr[0] == addr2->addr[0] && + addr1->addr[1] == addr2->addr[1] && + addr1->addr[2] == addr2->addr[2] && + addr1->addr[3] == addr2->addr[3]); +} + +void +ip_addr_set(struct ip_addr *dest, struct ip_addr *src) +{ + SMEMCPY(dest, src, sizeof(struct ip_addr)); + /* dest->addr[0] = src->addr[0]; + dest->addr[1] = src->addr[1]; + dest->addr[2] = src->addr[2]; + dest->addr[3] = src->addr[3];*/ +} + +u8_t +ip_addr_isany(struct ip_addr *addr) +{ + if (addr == NULL) return 1; + return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0); +} diff --git a/lab/net/lwip/core/mem.c b/lab/net/lwip/core/mem.c new file mode 100644 index 0000000..1e376b4 --- /dev/null +++ b/lab/net/lwip/core/mem.c @@ -0,0 +1,631 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" + +#include + +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * This structure is used to save the pool one element came from. + */ +struct mem_helper +{ + memp_t poolnr; +}; + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + struct mem_helper *element; + memp_t poolnr; + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr++) { + /* is this pool big enough to hold an element of the required size + plus a struct mem_helper that saves the pool this element came from? */ + if ((size + sizeof(struct mem_helper)) <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct mem_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ + /** @todo: we could try a bigger pool if this one is empty! */ + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct mem_helper */ + element++; + + return element; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct mem_helper *hmem = (struct mem_helper*)rmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct mem_helper */ + hmem--; + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[next]) of the next struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; +}; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** the heap. we need one struct mem at the end and some room for alignment */ +static u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +static sys_sem_t mem_sem; + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_arch_sem_wait(mem_sem, 0) +#define LWIP_MEM_FREE_UNPROTECT() sys_sem_signal(mem_sem) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_realloc() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)&ram[nmem->next])->prev = (u8_t *)mem - ram; + } + + /* plug hole backward */ + pmem = (struct mem *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram; + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = LWIP_MEM_ALIGN(ram_heap); + /* initialize the start of the heap */ + mem = (struct mem *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + mem_sem = sys_sem_new(1); + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - ((u8_t *)mem - ram)); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * In contrast to its name, mem_realloc can only shrink memory, not expand it. + * Since the only use (for now) is in pbuf_realloc (which also can only shrink), + * this shouldn't be a problem! + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_realloc(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_realloc: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (u8_t *)mem - ram; + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_realloc can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + MEM_STATS_DEC_USED(used, (size - newsize)); + + mem2 = (struct mem *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)&ram[ptr2]; + } + mem2 = (struct mem *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_arch_sem_wait(mem_sem, 0); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)&ram[ptr])->next) { + mem = (struct mem *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + local_mem_free_count = mem_free_count; + } + mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - ((u8_t *)mem - ram)); + } + + if (mem == lfree) { + /* Find next free block after mem and update lowest free pointer */ + while (lfree->used && lfree != ram_end) { + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + lfree = (struct mem *)&ram[lfree->next]; + } + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_sem_signal(mem_sem); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_sem_signal(mem_sem); + return NULL; +} + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/lab/net/lwip/core/memp.c b/lab/net/lwip/core/memp.c new file mode 100644 index 0000000..16a9189 --- /dev/null +++ b/lab/net/lwip/core/memp.c @@ -0,0 +1,370 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" + +#include + +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS +static +#endif +const u16_t memp_sizes[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +#ifdef LWIP_DEBUG +static const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (desc), +#include "lwip/memp_std.h" +}; +#endif /* LWIP_DEBUG */ + +/** This is the actual memory used by the pools. */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle + */ +static int +memp_sanity(void) +{ + s16_t i, c; + struct memp *m, *n; + + for (i = 0; i < MEMP_MAX; i++) { + for (m = memp_tab[i]; m != NULL; m = m->next) { + c = 1; + for (n = memp_tab[i]; n != NULL; n = n->next) { + if (n == m && --c < 0) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_size the element size of the pool p comes from + */ +static void +memp_overflow_check_element(struct memp *p, u16_t memp_size) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + LWIP_ASSERT("detected memp underflow!", 0); + } + } +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_size - MEMP_SANITY_REGION_AFTER_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + LWIP_ASSERT("detected memp overflow!", 0); + } + } +#endif +} + +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + + p = LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element(p, memp_sizes[i]); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i]); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + + p = LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i] - MEMP_SANITY_REGION_AFTER_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i]); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + struct memp *memp; + u16_t i, j; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } + + memp = LWIP_MEM_ALIGN(memp_memory); + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]); + } + } +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + + memp = memp_tab[type]; + + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); + + return memp; +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + if (mem == NULL) { + return; + } + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element(memp, memp_sizes[type]); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + + MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +} diff --git a/lab/net/lwip/core/netif.c b/lab/net/lwip/core/netif.c new file mode 100644 index 0000000..819dab7 --- /dev/null +++ b/lab/net/lwip/core/netif.c @@ -0,0 +1,663 @@ +/** + * @file + * lwIP network interface abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) { if (n->status_callback) (n->status_callback)(n); } +#else +#define NETIF_STATUS_CALLBACK(n) { /* NOP */ } +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) { if (n->link_callback) (n->link_callback)(n); } +#else +#define NETIF_LINK_CALLBACK(n) { /* NOP */ } +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + static u8_t netifnum = 0; + + /* reset new interface configuration state */ + netif->ip_addr.addr = 0; + netif->netmask.addr = 0; + netif->gw.addr = 0; + netif->flags = 0; +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + netif->num = netifnum++; + netif->input = input; +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start( netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void netif_remove(struct netif * netif) +{ + if ( netif == NULL ) return; + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop( netif); + } +#endif /* LWIP_IGMP */ + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + snmp_dec_iflist(); + } + else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + snmp_dec_iflist(); + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + /* this netif is default? */ + if (netif_default == netif) + /* reset default netif */ + netif_set_default(NULL); + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) + { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if ((!(ip_addr_isany(&(lpcb->local_ip)))) && + (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(&(lpcb->local_ip), ipaddr); + } + } + } +#endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ + ip_addr_set(&(netif->ip_addr), ipaddr); + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1(&netif->ip_addr), + ip4_addr2(&netif->ip_addr), + ip4_addr3(&netif->ip_addr), + ip4_addr4(&netif->ip_addr))); +} + +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, struct ip_addr *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1(&netif->gw), + ip4_addr2(&netif->gw), + ip4_addr3(&netif->gw), + ip4_addr4(&netif->gw))); +} + +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, struct ip_addr *netmask) +{ + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ + ip_addr_set(&(netif->netmask), netmask); + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1(&netif->netmask), + ip4_addr2(&netif->netmask), + ip4_addr3(&netif->netmask), + ip4_addr4(&netif->netmask))); +} + +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) + { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } + else + { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + if ( !(netif->flags & NETIF_FLAG_UP )) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_LINK_CALLBACK(netif); + NETIF_STATUS_CALLBACK(netif); + +#if LWIP_ARP + /** For Ethernet network interfaces, we would like to send a + * "gratuitous ARP"; this is an ARP packet sent by a node in order + * to spontaneously cause other nodes to update an entry in their + * ARP cache. From RFC 3220 "IP Mobility Support for IPv4" section 4.6. + */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_query(netif, &(netif->ip_addr), NULL); + } +#endif /* LWIP_ARP */ + + } +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + if ( netif->flags & NETIF_FLAG_UP ) + { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + + NETIF_LINK_CALLBACK(netif); + NETIF_STATUS_CALLBACK(netif); + } +} + +/** + * Ask if an interface is up + */ +u8_t netif_is_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_UP)?1:0; +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, void (* status_callback)(struct netif *netif )) +{ + if ( netif ) + netif->status_callback = status_callback; +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_ARP + /** For Ethernet network interfaces, we would like to send a + * "gratuitous ARP"; this is an ARP packet sent by a node in order + * to spontaneously cause other nodes to update an entry in their + * ARP cache. From RFC 3220 "IP Mobility Support for IPv4" section 4.6. + */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_query(netif, &(netif->ip_addr), NULL); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + + NETIF_LINK_CALLBACK(netif); +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); +} + +/** + * Ask if a link is up + */ +u8_t netif_is_link_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_LINK_UP) ? 1 : 0; +} + +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif )) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + r = NULL; + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + r = NULL; + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback(netif_poll, netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if(in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while(in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if(in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if(in != NULL) { + /* loopback packets are always IP packets! */ + if(ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while(netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ diff --git a/lab/net/lwip/core/pbuf.c b/lab/net/lwip/core/pbuf.c new file mode 100644 index 0000000..933107d --- /dev/null +++ b/lab/net/lwip/core/pbuf.c @@ -0,0 +1,779 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" + +#include + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + offset = 0; + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + /* add room for IP layer header */ + offset += PBUF_IP_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + /* add room for link layer header */ + offset += PBUF_LINK_HLEN; + break; + case PBUF_RAW: + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 3, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); + LWIP_ASSERT("mem_realloc give q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) + return 0; + + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { + LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, + (void *)(p + 1)));\ + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + return 1; + } + } + else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 2, ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 3, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF) { + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_FRESH | 2, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_STATE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_STATE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 3, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + LWIP_ASSERT("p_to != NULL", p_to != NULL); + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + } + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE | 1, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough) + * @param offset offset into the packet buffer from where to begin copying len bytes + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("netbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("netbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} diff --git a/lab/net/lwip/core/raw.c b/lab/net/lwip/core/raw.c new file mode 100644 index 0000000..5131c70 --- /dev/null +++ b/lab/net/lwip/core/raw.c @@ -0,0 +1,336 @@ +/** + * @file + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" + +#include + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs; + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb, *prev; + struct ip_hdr *iphdr; + s16_t proto; + u8_t eaten = 0; + + LWIP_UNUSED_ARG(inp); + + iphdr = p->payload; + proto = IPH_PROTO(iphdr); + + prev = NULL; + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if (pcb->protocol == proto) { + /* receive callback function available? */ + if (pcb->recv != NULL) { + /* the receive callback function did not eat the packet? */ + if (pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src)) != 0) + { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } + } + /* no receive callback function was set for this raw PCB */ + /* drop the packet */ + } + prev = pcb; + pcb = pcb->next; + } + return eaten; +} + +/** + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t +raw_bind(struct raw_pcb *pcb, struct ip_addr *ipaddr) +{ + ip_addr_set(&pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t +raw_connect(struct raw_pcb *pcb, struct ip_addr *ipaddr) +{ + ip_addr_set(&pcb->remote_ip, ipaddr); + return ERR_OK; +} + + +/** + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + * + * @return non-zero if the packet was free()d, zero if the packet remains + * available for others. + */ +void +raw_recv(struct raw_pcb *pcb, + u8_t (* recv)(void *arg, struct raw_pcb *upcb, struct pbuf *p, + struct ip_addr *addr), + void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr) +{ + err_t err; + struct netif *netif; + struct ip_addr *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | 3, ("raw_sendto\n")); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, IP_HLEN)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | 2, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + if(pbuf_header(q, -IP_HLEN)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + if ((netif = ip_route(ipaddr)) == NULL) { + LWIP_DEBUGF(RAW_DEBUG | 1, ("raw_sendto: No route to 0x%"X32_F"\n", ipaddr->addr)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * + */ +err_t +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, &pcb->remote_ip); +} + +/** + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else { + for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new(u8_t proto) { + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | 3, ("raw_new\n")); + + pcb = memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +#endif /* LWIP_RAW */ diff --git a/lab/net/lwip/core/snmp/asn1_dec.c b/lab/net/lwip/core/snmp/asn1_dec.c new file mode 100644 index 0000000..650fb40 --- /dev/null +++ b/lab/net/lwip/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, upto 64k + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/lab/net/lwip/core/snmp/asn1_enc.c b/lab/net/lwip/core/snmp/asn1_enc.c new file mode 100644 index 0000000..77af6b4 --- /dev/null +++ b/lab/net/lwip/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = length; + } + else + { + /* most significant length octet */ + *msg_ptr = length >> 8; + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = value >> (octets_needed << 3); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = value >> (octets_needed << 3); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (ident[0] * 40) + ident[1]; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = sub_id >> shift; + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/lab/net/lwip/core/snmp/mib2.c b/lab/net/lwip/core/snmp/mib2.c new file mode 100644 index 0000000..c5c2f36 --- /dev/null +++ b/lab/net/lwip/core/snmp/mib2.c @@ -0,0 +1,4126 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "netif/etharp.h" + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node* const)&udp_root, (struct mib_node* const)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node* const)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node* const)&udp_scalar, (struct mib_node* const)&udp_scalar, + (struct mib_node* const)&udp_scalar, (struct mib_node* const)&udp_scalar, + (struct mib_node* const)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node* const)&tcpconntree_root, (struct mib_node* const)&tcpconntree_root, + (struct mib_node* const)&tcpconntree_root, (struct mib_node* const)&tcpconntree_root, + (struct mib_node* const)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node* const)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcpconntable, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node* const)&ipntomtree_root, (struct mib_node* const)&ipntomtree_root, + (struct mib_node* const)&ipntomtree_root, (struct mib_node* const)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node* const)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node* const)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node* const)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ipaddrtable, + (struct mib_node* const)&iprtetable, (struct mib_node* const)&ipntomtable, + (struct mib_node* const)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node* const)&arptree_root, + (struct mib_node* const)&arptree_root, + (struct mib_node* const)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node* const)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node* const)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node* const)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node* const)&interfaces_scalar, (struct mib_node* const)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node* const)&sys_tem, + (struct mib_node* const)&interfaces, + (struct mib_node* const)&at, + (struct mib_node* const)&mib2_ip, + (struct mib_node* const)&icmp, +#if LWIP_TCP + (struct mib_node* const)&tcp, +#endif + (struct mib_node* const)&udp, + (struct mib_node* const)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node* const)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#if SNMP_PRIVATE_MIB +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node* const)&mgmt, (struct mib_node* const)&private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node* const)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default; +static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + + +/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */ +/** + * Copy octet string. + * + * @param dst points to destination + * @param src points to source + * @param n number of octets to copy. + */ +void ocstrncpy(u8_t *dst, u8_t *src, u8_t n) +{ + while (n > 0) + { + n--; + *dst++ = *src++; + } +} + +/** + * Copy object identifier (s32_t) array. + * + * @param dst points to destination + * @param src points to source + * @param n number of sub identifiers to copy. + */ +void objectidncpy(s32_t *dst, s32_t *src, u8_t n) +{ + while(n > 0) + { + n--; + *dst++ = *src++; + } +} + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdesr(u8_t *str, u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid) +{ + *oid = &sysobjid; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(struct snmp_obj_id *oid) +{ + sysobjid = *oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, struct ip_addr *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + struct ip_addr hip; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + hip.addr = ntohl(ip->addr); + snmp_iptooid(&hip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, struct ip_addr *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + struct ip_addr hip; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + hip.addr = ntohl(ip->addr); + snmp_iptooid(&hip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + struct ip_addr ip; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + ip.addr = ntohl(ni->ip_addr.addr); + snmp_iptooid(&ip, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + struct ip_addr ip; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + ip.addr = ntohl(ni->ip_addr.addr); + snmp_iptooid(&ip, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + struct ip_addr dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + dst.addr = 0; + insert = 1; + } + else + { + /* route to the network address */ + dst.addr = ntohl(ni->ip_addr.addr & ni->netmask.addr); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (dst.addr != 0) insert = 1; + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t delete = 0; + struct ip_addr dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + dst.addr = 0; + delete = 1; + } + else + { + /* route to the network address */ + dst.addr = ntohl(ni->ip_addr.addr & ni->netmask.addr); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (dst.addr != 0) delete = 1; + } + if (delete) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + struct ip_addr ip; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + ip.addr = ntohl(pcb->local_ip.addr); + snmp_iptooid(&ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + struct ip_addr ip; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + ip.addr = ntohl(pcb->local_ip.addr); + snmp_iptooid(&ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + pcb = udp_pcbs; + while ((pcb != NULL)) + { + if ((pcb->local_ip.addr == ip.addr) && + (pcb->local_port == udpidx[4])) + { + bindings++; + } + pcb = pcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + if (ident_len){} + if (ident){} + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + if (od){} + if (len){} + if (value){} +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + if (od){} + if (len){} + if (value){} + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + if (od){} + if (len){} + if (value){} +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid.len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + ocstrncpy(value,sysdescr_ptr,len); + break; + case 2: /* sysObjectID */ + objectidncpy((s32_t*)value,(s32_t*)sysobjid.id,len / sizeof(s32_t)); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime(value); + } + break; + case 4: /* sysContact */ + ocstrncpy(value,syscontact_ptr,len); + break; + case 5: /* sysName */ + ocstrncpy(value,sysname_ptr,len); + break; + case 6: /* sysLocation */ + ocstrncpy(value,syslocation_ptr,len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = value; + *sint_ptr = sysservices; + } + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + if (value) {} + set_ok = 0; + id = od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + id = od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + ocstrncpy(syscontact_ptr,value,len); + *syscontact_len_ptr = len; + break; + case 5: /* sysName */ + ocstrncpy(sysname_ptr,value,len); + *sysname_len_ptr = len; + break; + case 6: /* sysLocation */ + ocstrncpy(syslocation_ptr,value,len); + *syslocation_len_ptr = len; + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + if (len){} + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + ocstrncpy(value,(u8_t*)netif->name,len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + ocstrncpy(value,netif->hwaddr,len); + break; + case 7: /* ifAdminStatus */ +#if LWIP_NETIF_LINK_CALLBACK + { + s32_t *sint_ptr = value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; +#endif + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + objectidncpy((s32_t*)value,(s32_t*)ifspecific.id,len / sizeof(s32_t)); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test (struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value (struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + struct ip_addr* ipaddr_ret; +#endif /* LWIP_ARP */ + struct ip_addr ip; + struct netif *netif; + + if (len) {} + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + ip.addr = htonl(ip.addr); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + struct ip_addr *dst = value; + + *dst = *ipaddr_ret; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + if (len) {} + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = value; + *uint_ptr = iproutingdiscards; + } + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = value; + + if (len) {} + set_ok = 0; + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + struct ip_addr ip; + struct netif *netif = netif_list; + + if (len) {} + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ip.addr = htonl(ip.addr); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + struct ip_addr *dst = value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + struct ip_addr *dst = value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = ip_addr_broadcast.addr & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + struct ip_addr dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + dest.addr = htonl(dest.addr); + + if (dest.addr == 0) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + id = ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte has 0.0.0.0 dest */ + dst->addr = 0; + } + else + { + /* netifs have netaddress dest */ + dst->addr = netif->ip_addr.addr & netif->netmask.addr; + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = value; + + if (dest.addr == 0) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = value; + + if (dest.addr == 0) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte use 0.0.0.0 mask */ + dst->addr = 0; + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + objectidncpy((s32_t*)value,(s32_t*)iprouteinfo.id,len / sizeof(s32_t)); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + struct ip_addr* ipaddr_ret; +#endif /* LWIP_ARP */ + struct ip_addr ip; + struct netif *netif; + + if (len) {} + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + ip.addr = htonl(ip.addr); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + struct ip_addr *dst = value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + if (len){} + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + s32_t *sint_ptr = value; + u8_t id; + + if (len){} + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct ip_addr lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lip.addr = htonl(lip.addr); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rip.addr = htonl(rip.addr); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + if (len){} + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + struct ip_addr ip; + u16_t port; + + if (len){} + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ip.addr = htonl(ip.addr); + port = od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !((pcb->local_ip.addr == ip.addr) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + struct ip_addr *dst = value; + *dst = pcb->local_ip; + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = value; + *sint_ptr = pcb->local_port; + } + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + if (len){} + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + if (len) {} + set_ok = 0; + id = od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + if (len) {} + id = od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = value; + *snmpenableauthentraps_ptr = *sint_ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/lab/net/lwip/core/snmp/mib_structs.c b/lab/net/lwip/core/snmp/mib_structs.c new file mode 100644 index 0000000..af8994e --- /dev/null +++ b/lab/net/lwip/core/snmp/mib_structs.c @@ -0,0 +1,1183 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/mem.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + u16_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while (nif != netif) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, struct ip_addr *ip) +{ + u32_t ipa; + + ipa = ident[0]; + ipa <<= 8; + ipa |= ident[1]; + ipa <<= 8; + ipa |= ident[2]; + ipa <<= 8; + ipa |= ident[3]; + ip->addr = ipa; +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(struct ip_addr *ip, s32_t *ident) +{ + u32_t ipa; + + ipa = ip->addr; + ident[0] = (ipa >> 24) & 0xff; + ident[1] = (ipa >> 16) & 0xff; + ident[2] = (ipa >> 8) & 0xff; + ident[3] = ipa & 0xff; +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)mem_malloc(sizeof(struct mib_list_node)); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + mem_free(ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)mem_malloc(sizeof(struct mib_list_rootnode)); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + mem_free(lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (rerurn) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + j = i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u8_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + struct nse cur_node; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + j = i + 1; + if (j < len) + { + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/lab/net/lwip/core/snmp/msg_in.c b/lab/net/lwip/core/snmp/msg_in.c new file mode 100644 index 0000000..e2f177f --- /dev/null +++ b/lab/net/lwip/core/snmp/msg_in.c @@ -0,0 +1,1453 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + msg_ps->error_index = 1 + msg_ps->vb_idx; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + vb->value_len = msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + vb->value = mem_malloc(vb->value_len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + mem_free(vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + vb->value_len = object_def.v_len; + if (vb->value_len > 0) + { + vb->value = mem_malloc(vb->value_len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + mem_free(vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + vb = snmp_varbind_alloc(&oid, object_def.asn_type, object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access == MIB_OBJECT_READ_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() @todo: use reply value?? */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access == MIB_OBJECT_READ_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + struct udp_hdr *udphdr; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + /* peek in the UDP header (goto IP payload) */ + if(pbuf_header(p, UDP_HLEN)){ + LWIP_ASSERT("Can't move to UDP header", 0); + pbuf_free(p); + return; + } + udphdr = p->payload; + + /* check if datagram is really directed at us (including broadcast requests) */ + if ((pcb == snmp1_pcb) && (ntohs(udphdr->dest) == SNMP_IN_PORT)) + { + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idxstate != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx != SNMP_CONCURRENT_REQUESTS) + { + err_t err_ret; + u16_t payload_len; + u16_t payload_ofs; + u16_t varbind_ofs = 0; + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + /* read UDP payload length from UDP header */ + payload_len = ntohs(udphdr->len) - UDP_HLEN; + + /* adjust to UDP payload */ + payload_ofs = UDP_HLEN; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + if (((msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) || + (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) || + (msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)) && + ((msg_ps->error_status == SNMP_ES_NOERROR) && + (msg_ps->error_index == 0)) ) + { + /* Only accept requests and requests without error (be robust) */ + err_ret = err_ret; + } + else + { + /* Reject response and trap headers or error requests as input! */ + err_ret = ERR_ARG; + } + if (err_ret == ERR_OK) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + if ((err_ret == ERR_OK) && (msg_ps->invb.count > 0)) + { + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); + } + else + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + } + } + else + { + /* header check failed + drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + } + } + else + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + } + } + else + { + /* datagram not for us */ + pbuf_free(p); + } +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb = snmp_varbind_alloc(&oid, type, len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i); + LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL); + if (vb->ident == NULL) + { + mem_free(vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + /* allocate raw bytes for our object value */ + vb->value = mem_malloc(len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value == NULL) + { + if (vb->ident != NULL) + { + mem_free(vb->ident); + } + mem_free(vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + mem_free(vb->value); + } + if (vb->ident != NULL ) + { + mem_free(vb->ident); + } + mem_free(vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/lab/net/lwip/core/snmp/msg_out.c b/lab/net/lwip/core/snmp/msg_out.c new file mode 100644 index 0000000..b705aac --- /dev/null +++ b/lab/net/lwip/core/snmp/msg_out.c @@ -0,0 +1,683 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + struct ip_addr dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, struct ip_addr *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].dip.addr = htonl(dst->addr); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + if (m_stat->error_status == SNMP_ES_TOOBIG) + { + snmp_varbind_list_enc(&emptyvb, p, ofs); + } + else + { + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + } + + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterpise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + struct ip_addr dst_ip; + struct pbuf *p; + u16_t i,tot_len; + + for (i=0, td = &trap_dst[0]; ienable != 0) && (td->dip.addr != 0)) + { + /* network order trap destination */ + trap_msg.dip.addr = td->dip.addr; + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + dst_ip.addr = ntohl(dst_if->ip_addr.addr); + trap_msg.sip_raw[0] = dst_ip.addr >> 24; + trap_msg.sip_raw[1] = dst_ip.addr >> 16; + trap_msg.sip_raw[2] = dst_ip.addr >> 8; + trap_msg.sip_raw[3] = dst_ip.addr; + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** connect to the TRAP destination */ + udp_connect(trap_msg.pcb, &trap_msg.dip, SNMP_TRAP_PORT); + udp_send(trap_msg.pcb, p); + /** disassociate remote address and port with this pcb */ + udp_disconnect(trap_msg.pcb); + + pbuf_free(p); + } + else + { + return ERR_MEM; + } + } + } + return ERR_OK; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required lenght for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required lenght for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required lenght for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/lab/net/lwip/core/stats.c b/lab/net/lwip/core/stats.c new file mode 100644 index 0000000..a036d83 --- /dev/null +++ b/lab/net/lwip/core/stats.c @@ -0,0 +1,149 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" + +#include + +struct stats_ lwip_stats; + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp) +{ + LWIP_PLATFORM_DIAG(("\nIGMP\n\t")); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("v1_rxed: %"STAT_COUNTER_F"\n\t", igmp->v1_rxed)); + LWIP_PLATFORM_DIAG(("join_sent: %"STAT_COUNTER_F"\n\t", igmp->join_sent)); + LWIP_PLATFORM_DIAG(("leave_sent: %"STAT_COUNTER_F"\n\t", igmp->leave_sent)); + LWIP_PLATFORM_DIAG(("unicast_query: %"STAT_COUNTER_F"\n\t", igmp->unicast_query)); + LWIP_PLATFORM_DIAG(("report_sent: %"STAT_COUNTER_F"\n\t", igmp->report_sent)); + LWIP_PLATFORM_DIAG(("report_rxed: %"STAT_COUNTER_F"\n\t", igmp->report_rxed)); + LWIP_PLATFORM_DIAG(("group_query_rxed: %"STAT_COUNTER_F"\n", igmp->group_query_rxed)); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/lab/net/lwip/core/sys.c b/lab/net/lwip/core/sys.c new file mode 100644 index 0000000..d1fbda4 --- /dev/null +++ b/lab/net/lwip/core/sys.c @@ -0,0 +1,344 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +/** + * Struct used for sys_sem_wait_timeout() to tell wether the time + * has run out or the semaphore has really become available. + */ +struct sswt_cb +{ + s16_t timeflag; + sys_sem_t *psem; +}; + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts (for this thread) are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_mbox_fetch(sys_mbox_t mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeouts *timeouts; + struct sys_timeo *tmptimeout; + sys_timeout_handler h; + void *arg; + + again: + timeouts = sys_arch_timeouts(); + + if (!timeouts || !timeouts->next) { + UNLOCK_TCPIP_CORE(); + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + LOCK_TCPIP_CORE(); + } else { + if (timeouts->next->time > 0) { + UNLOCK_TCPIP_CORE(); + time_needed = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + LOCK_TCPIP_CORE(); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (h != NULL) { + LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", (void*)&h, arg)); + h(arg); + } + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < timeouts->next->time) { + timeouts->next->time -= time_needed; + } else { + timeouts->next->time = 0; + } + } + } +} + +/** + * Wait (forever) for a semaphore to become available. + * While waiting, timeouts (for this thread) are processed. + * + * @param sem semaphore to wait for + */ +void +sys_sem_wait(sys_sem_t sem) +{ + u32_t time_needed; + struct sys_timeouts *timeouts; + struct sys_timeo *tmptimeout; + sys_timeout_handler h; + void *arg; + + again: + + timeouts = sys_arch_timeouts(); + + if (!timeouts || !timeouts->next) { + sys_arch_sem_wait(sem, 0); + } else { + if (timeouts->next->time > 0) { + time_needed = sys_arch_sem_wait(sem, timeouts->next->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (h != NULL) { + LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", (void*)&h, (void *)arg)); + h(arg); + } + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < timeouts->next->time) { + timeouts->next->time -= time_needed; + } else { + timeouts->next->time = 0; + } + } + } +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_mbox_fetch() + * - while waiting for a semaphore using sys_sem_wait() or sys_sem_wait_timeout() + * - while sleeping using the inbuilt sys_msleep() + * + * @param msecs time in milliseconds after that the timer should expire + * @param h callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +void +sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *timeout, *t; + + timeout = memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = h; + timeout->arg = arg; + timeout->time = msecs; + + timeouts = sys_arch_timeouts(); + + LWIP_DEBUGF(SYS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n", + (void *)timeout, msecs, (void*)&h, (void *)arg)); + + if (timeouts == NULL) { + LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL); + return; + } + + if (timeouts->next == NULL) { + timeouts->next = timeout; + return; + } + + if (timeouts->next->time > msecs) { + timeouts->next->time -= msecs; + timeout->next = timeouts->next; + timeouts->next = timeout; + } else { + for(t = timeouts->next; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'h' in the list of timeouts. + * + * @param h callback function that would be called by the timeout + * @param arg callback argument that would be passed to h +*/ +void +sys_untimeout(sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *prev_t, *t; + + timeouts = sys_arch_timeouts(); + + if (timeouts == NULL) { + LWIP_ASSERT("sys_untimeout: timeouts != NULL", timeouts != NULL); + return; + } + if (timeouts->next == NULL) { + return; + } + + for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == h) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) + timeouts->next = t->next; + else + prev_t->next = t->next; + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) + t->next->time += t->time; + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +/** + * Timeout handler function for sys_sem_wait_timeout() + * + * @param arg struct sswt_cb* used to signal a semaphore and end waiting. + */ +static void +sswt_handler(void *arg) +{ + struct sswt_cb *sswt_cb = (struct sswt_cb *) arg; + + /* Timeout. Set flag to TRUE and signal semaphore */ + sswt_cb->timeflag = 1; + sys_sem_signal(*(sswt_cb->psem)); +} + +/** + * Wait for a semaphore with timeout (specified in ms) + * + * @param sem semaphore to wait + * @param timeout timeout in ms (0: wait forever) + * @return 0 on timeout, 1 otherwise + */ +int +sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout) +{ + struct sswt_cb sswt_cb; + + sswt_cb.psem = &sem; + sswt_cb.timeflag = 0; + + /* If timeout is zero, then just wait forever */ + if (timeout > 0) { + /* Create a timer and pass it the address of our flag */ + sys_timeout(timeout, sswt_handler, &sswt_cb); + } + sys_sem_wait(sem); + /* Was it a timeout? */ + if (sswt_cb.timeflag) { + /* timeout */ + return 0; + } else { + /* Not a timeout. Remove timeout entry */ + sys_untimeout(sswt_handler, &sswt_cb); + return 1; + } +} + +/** + * Sleep for some ms. Timeouts are processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + sys_sem_t delaysem = sys_sem_new(0); + + sys_sem_wait_timeout(delaysem, ms); + + sys_sem_free(delaysem); +} + + +#endif /* NO_SYS */ diff --git a/lab/net/lwip/core/tcp.c b/lab/net/lwip/core/tcp.c new file mode 100644 index 0000000..0c2c68f --- /dev/null +++ b/lab/net/lwip/core/tcp.c @@ -0,0 +1,1420 @@ +/** + * @file + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/snmp.h" +#include "lwip/tcp.h" + +#include + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +struct tcp_pcb *tcp_tmp_pcb; + +static u8_t tcp_timer; +static u16_t tcp_new_port(void); + +/** + * Called periodically to dispatch TCP timers. + * + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ + err_t err; + +#if TCP_DEBUG + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + err = ERR_OK; + TCP_RMV(&tcp_bound_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent when tcp_close returns. */ + tcp_output(pcb); + } + return err; +} + +/** + * Aborts a connection by sending a RST to the remote host and deletes + * the local protocol control block. This is done when a connection is + * killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + u32_t seqno, ackno; + u16_t remote_port, local_port; + struct ip_addr remote_ip, local_ip; +#if LWIP_CALLBACK_API + void (* errf)(void *arg, err_t err); +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; + ip_addr_set(&local_ip, &(pcb->local_ip)); + ip_addr_set(&remote_ip, &(pcb->remote_ip)); + local_port = pcb->local_port; + remote_port = pcb->remote_port; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abort: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) +{ + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_connect: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + if (port == 0) { + port = tcp_new_port(); + } + /* Check if the address already is in use. */ + /* Check the listen pcbs. */ + for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* Check the connected pcbs. */ + for(cpcb = tcp_active_pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* Check the bound, not yet connected pcbs. */ + for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah), + * we have to check the pcbs in TIME-WAIT state, also: */ + for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + + if (!ip_addr_isany(ipaddr)) { + pcb->local_ip = *ipaddr; + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb_listen *lpcb; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } + lpcb = memp_malloc(MEMP_TCP_PCB_LISTEN); + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->local_port = pcb->local_port; + lpcb->state = LISTEN; + lpcb->so_options = pcb->so_options; + lpcb->so_options |= SOF_ACCEPTCONN; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; + ip_addr_set(&lpcb->local_ip, &pcb->local_ip); + TCP_RMV(&tcp_bound_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null; +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb); + return (struct tcp_pcb *)lpcb; +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + if ((u32_t)pcb->rcv_wnd + len > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + } else { + pcb->rcv_wnd += len; + if (pcb->rcv_wnd >= pcb->mss) { + pcb->rcv_ann_wnd = pcb->rcv_wnd; + } + } + + if (!(pcb->flags & TF_ACK_DELAY) && + !(pcb->flags & TF_ACK_NOW)) { + /* + * We send an ACK here (if one is not already pending, hence + * the above tests) as tcp_recved() implies that the application + * has processed some data, and so we can open the receiver's + * window to allow more to be transmitted. This could result in + * two ACKs being sent for each received packet in some limited cases + * (where the application is only receiving data, and is slow to + * process it) but it is necessary to guarantee that the sender can + * continue to transmit. + */ + tcp_ack(pcb); + } + else if (pcb->flags & TF_ACK_DELAY && pcb->rcv_wnd >= TCP_WND/2) { + /* If we can send a window update such that there is a full + * segment available in the window, do so now. This is sort of + * nagle-like in its goals, and tries to hit a compromise between + * sending acks each time the window is updated, and only sending + * window updates when a timer expires. The "threshold" used + * above (currently TCP_WND/2) can be tuned to be more or less + * aggressive */ + tcp_ack_now(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * A nastly hack featuring 'goto' statements that allocates a + * new TCP local port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + struct tcp_pcb *pcb; +#ifndef TCP_LOCAL_PORT_RANGE_START +#define TCP_LOCAL_PORT_RANGE_START 4096 +#define TCP_LOCAL_PORT_RANGE_END 0x7fff +#endif + static u16_t port = TCP_LOCAL_PORT_RANGE_START; + + again: + if (++port > TCP_LOCAL_PORT_RANGE_END) { + port = TCP_LOCAL_PORT_RANGE_START; + } + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + return port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port, + err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) +{ + u32_t optdata; + err_t ret; + u32_t iss; + + LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + pcb->remote_ip = *ipaddr; + } else { + return ERR_VAL; + } + pcb->remote_port = port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + } + iss = tcp_next_iss(); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; + pcb->state = SYN_SENT; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#endif /* LWIP_CALLBACK_API */ + TCP_RMV(&tcp_bound_pcbs, pcb); + TCP_REG(&tcp_active_pcbs, pcb); + + snmp_inc_tcpactiveopens(); + + /* Build an MSS option */ + optdata = TCP_BUILD_MSS_OPTION(); + + ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4); + if (ret == ERR_OK) { + tcp_output(pcb); + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *pcb2, *prev; + u16_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + + pcb_remove = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); + } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) + ++pcb->rtime; + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < pcb->mss) { + pcb->ssthresh = pcb->mss * 2; + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + + /* Check if KEEPALIVE should be sent */ + if((pcb->so_options & SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { +#if LWIP_TCP_KEEPALIVE + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl)) + / TCP_SLOW_INTERVAL) +#else + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + tcp_abort(pcb); + } +#if LWIP_TCP_KEEPALIVE + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl) + / TCP_SLOW_INTERVAL) +#else + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT) + / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); + + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + + /* We check if we should poll the connection. */ + ++pcb->polltmr; + if (pcb->polltmr >= pcb->pollinterval) { + pcb->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + TCP_EVENT_POLL(pcb, err); + if (err == ERR_OK) { + tcp_output(pcb); + } + } + + prev = pcb; + pcb = pcb->next; + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + err_t err; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } + } + + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + } +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + * @return the number of pbufs that were deallocated + */ +u8_t +tcp_segs_free(struct tcp_seg *seg) +{ + u8_t count = 0; + struct tcp_seg *next; + while (seg != NULL) { + next = seg->next; + count += tcp_seg_free(seg); + seg = next; + } + return count; +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + * @return the number of pbufs that were deallocated + */ +u8_t +tcp_seg_free(struct tcp_seg *seg) +{ + u8_t count = 0; + + if (seg != NULL) { + if (seg->p != NULL) { + count = pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } + return count; +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} +#if TCP_QUEUE_OOSEQ + +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +static err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + arg = arg; + if (p != NULL) { + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has lower priority than prio. + * + * @param prio minimum priority + */ +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Allocate a new tcp_pcb structure. + * + * @param prio priority for the new pcb + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = memp_malloc(MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + memset(pcb, 0, sizeof(struct tcp_pcb)); + pcb->prio = TCP_PRIO_NORMAL; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_TTL; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; + iss = tcp_next_iss(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->snd_max = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; + + pcb->polltmr = 0; + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +/** + * Used to specify the argument that should be passed callback + * functions. + * + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)) +{ + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len)) +{ + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + * @param pcb tcp_pcb to set the err callback + * @param errf callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, + void (* errf)(void *arg, err_t err)) +{ + pcb->errf = errf; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) +{ + ((struct tcp_pcb_listen *)pcb)->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval) +{ +#if LWIP_CALLBACK_API + pcb->poll = poll; +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ /* LW */ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is also deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr) +{ + u16_t mss_s; + struct netif *outif; + + outif = ip_route(addr); + if ((outif != NULL) && (outif->mtu != 0)) { + mss_s = outif->mtu - IP_HLEN - TCP_HLEN; + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * but we only send options with SYN and that is never filled with data! */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: ")); + switch (s) { + case CLOSED: + LWIP_DEBUGF(TCP_DEBUG, ("CLOSED\n")); + break; + case LISTEN: + LWIP_DEBUGF(TCP_DEBUG, ("LISTEN\n")); + break; + case SYN_SENT: + LWIP_DEBUGF(TCP_DEBUG, ("SYN_SENT\n")); + break; + case SYN_RCVD: + LWIP_DEBUGF(TCP_DEBUG, ("SYN_RCVD\n")); + break; + case ESTABLISHED: + LWIP_DEBUGF(TCP_DEBUG, ("ESTABLISHED\n")); + break; + case FIN_WAIT_1: + LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_1\n")); + break; + case FIN_WAIT_2: + LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_2\n")); + break; + case CLOSE_WAIT: + LWIP_DEBUGF(TCP_DEBUG, ("CLOSE_WAIT\n")); + break; + case CLOSING: + LWIP_DEBUGF(TCP_DEBUG, ("CLOSING\n")); + break; + case LAST_ACK: + LWIP_DEBUGF(TCP_DEBUG, ("LAST_ACK\n")); + break; + case TIME_WAIT: + LWIP_DEBUGF(TCP_DEBUG, ("TIME_WAIT\n")); + break; + } +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/lab/net/lwip/core/tcp_in.c b/lab/net/lwip/core/tcp_in.c new file mode 100644 index 0000000..070ec7f --- /dev/null +++ b/lab/net/lwip/core/tcp_in.c @@ -0,0 +1,1350 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; +static struct tcp_hdr *tcphdr; +static struct ip_hdr *iphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static u8_t tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the IP header) + * @param inp network interface on which this segment was received + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; + u8_t hdrlen; + err_t err; + + PERF_START; + + TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); + + iphdr = p->payload; + tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* remove header from payload */ + if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if (ip_addr_isbroadcast(&(iphdr->dest), inp) || + ip_addr_ismulticast(&(iphdr->dest))) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_TCP, p->tot_len) != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), + IP_PROTO_TCP, p->tot_len))); +#if TCP_DEBUG + tcp_debug_print(tcphdr); +#endif /* TCP_DEBUG */ + TCP_STATS_INC(tcp.chkerr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } +#endif + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); + tcphdr->dest = ntohs(tcphdr->dest); + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); + tcphdr->wnd = ntohs(tcphdr->wnd); + + flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS; + tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0); + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { + + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((ip_addr_isany(&(lpcb->local_ip)) || + ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && + lpcb->local_port == tcphdr->dest) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; + } + prev = (struct tcp_pcb *)lpcb; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.dataptr = p->payload; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else { + /* drop incoming packets, because pcb is "full" */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + } + + tcp_input_pcb = pcb; + err = tcp_process(pcb); + tcp_input_pcb = NULL; + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err); + } + + if (recv_data != NULL) { + if(flags & TCP_PSH) { + recv_data->flags |= PBUF_FLAG_PUSH; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); + } + + /* If there were no errors, we try to send something out. */ + if (err == ERR_OK) { + tcp_output(pcb); + } + } + } + + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + inseg.p = NULL; + } +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + tcp_rst(ackno, seqno + tcplen, + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + u32_t optdata; + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno + 1, seqno + tcplen, + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } else if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ + npcb = tcp_alloc(pcb->prio); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ + ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); + npcb->local_port = pcb->local_port; + ip_addr_set(&(npcb->remote_ip), &(iphdr->src)); + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->snd_wnd = tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER); + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG(&tcp_active_pcbs, npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + snmp_inc_tcppassiveopens(); + + /* Build an MSS option. */ + optdata = TCP_BUILD_MSS_OPTION(); + /* Send a SYN|ACK together with the MSS option. */ + tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4); + return tcp_output(npcb); + } + return ERR_OK; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + if (TCP_SEQ_GT(seqno + tcplen, pcb->rcv_nxt)) { + pcb->rcv_nxt = seqno + tcplen; + } + if (tcplen > 0) { + tcp_ack_now(pcb); + } + return tcp_output(pcb); +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + u8_t accepted_inseq; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags = TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + /* Update the PCB (in)activity timer. */ + pcb->tmr = tcp_ticks; + pcb->keep_cnt_sent = 0; + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + + /* Parse any options in the SYNACK before using pcb->mss since that + * can be changed by the received options! */ + tcp_parseopt(pcb); +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + tcp_seg_free(rseg); + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK && + !(flags & TCP_RST)) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + tcp_abort(pcb); + return ERR_ABRT; + } + old_cwnd = pcb->cwnd; + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + accepted_inseq = tcp_receive(pcb); + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if ((flags & TCP_FIN) && accepted_inseq) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } + /* incorrect ACK number */ + else { + /* send RST */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + accepted_inseq = tcp_receive(pcb); + if ((flags & TCP_FIN) && accepted_inseq) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (flags & TCP_FIN) { + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags = TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * i it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + * + * @return 1 if the incoming segment is the next in sequence, 0 if not + */ +static u8_t +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + u8_t accepted_inseq = 0; + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl1; + + /* Update window. */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) { + pcb->persist_backoff = 0; + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %"U32_F" snd_max %"U32_F" ackno %"U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, pcb->snd_max, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + if (pcb->lastack == ackno) { + pcb->acked = 0; + + if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){ + ++pcb->dupacks; + if (pcb->dupacks >= 3 && pcb->unacked != NULL) { + if (!(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %"U16_F" (%"U32_F"), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + /* Set ssthresh to max (FlightSize / 2, 2*SMSS) */ + /*pcb->ssthresh = LWIP_MAX((pcb->snd_max - + pcb->lastack) / 2, + 2 * pcb->mss);*/ + /* Set ssthresh to half of the minimum of the current cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) + pcb->ssthresh = pcb->snd_wnd / 2; + else + pcb->ssthresh = pcb->cwnd / 2; + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: The minimum value for ssthresh %"U16_F" should be min 2 mss %"U16_F"...\n", pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } else { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } + } + } else { + LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %"U32_F" %"U32_F"\n", + pcb->snd_wl1 + pcb->snd_wnd, right_wnd_edge)); + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_max)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) { + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. */ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else + pcb->rtime = 0; + + pcb->polltmr = 0; + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + while (pcb->unsent != NULL && + /*TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), ackno) && + TCP_SEQ_LEQ(ackno, pcb->snd_max)*/ + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), pcb->snd_max) + ) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + if (pcb->unsent != NULL) { + pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further. */ + if (tcplen > 0) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){ + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + + off = pcb->rcv_nxt - seqno; + p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + /* KJM following line changed to use p->payload rather than inseg->p->payload + to fix bug #9076 */ + inseg.dataptr = p->payload; + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (pcb->rcv_nxt == seqno) { + accepted_inseq = 1; + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + inseg.len)) { + if (pcb->ooseq->len > 0) { + /* We have to trim the second edge of the incoming + segment. */ + inseg.len = (u16_t)(pcb->ooseq->tcphdr->seqno - seqno); + pbuf_realloc(inseg.p, inseg.len); + } else { + /* does the ooseq segment contain only flags that are in inseg also? */ + if ((TCPH_FLAGS(inseg.tcphdr) & (TCP_FIN|TCP_SYN)) == + (TCPH_FLAGS(pcb->ooseq->tcphdr) & (TCP_FIN|TCP_SYN))) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + memp_free(MEMP_TCP_SEG, old_ooseq); + } + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + tcplen = TCP_TCPLEN(&inseg); + + /* First received FIN will be ACKed +1, on any successive (duplicate) + * FINs we are already in CLOSE_WAIT and have already done +1. + */ + if (pcb->state != CLOSE_WAIT) { + pcb->rcv_nxt += tcplen; + } + + /* Update the receiver's (our) window. */ + if (pcb->rcv_wnd < tcplen) { + pcb->rcv_wnd = 0; + } else { + pcb->rcv_wnd -= tcplen; + } + + if (pcb->rcv_ann_wnd < tcplen) { + pcb->rcv_ann_wnd = 0; + } else { + pcb->rcv_ann_wnd -= tcplen; + } + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags = TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + is now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + if (pcb->rcv_wnd < TCP_TCPLEN(cseg)) { + pcb->rcv_wnd = 0; + } else { + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + } + if (pcb->rcv_ann_wnd < TCP_TCPLEN(cseg)) { + pcb->rcv_ann_wnd = 0; + } else { + pcb->rcv_ann_wnd -= TCP_TCPLEN(cseg); + } + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags = TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_ack_now(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) { + if (seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace the old segment with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + cseg->next = next->next; + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_seg_free(next); + if (cseg->next != NULL) { + next = cseg->next; + if (TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + + if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(inseg.p, inseg.len); + } + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + cseg->next = next; + pcb->ooseq = cseg; + } + break; + } + } else + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if(TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)){ + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim and insert the + incoming segment and trim the previous segment, if + needed. */ + if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(inseg.p, inseg.len); + } + + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + cseg->next = next; + prev->next = cseg; + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + } + break; + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + } + break; + } + } + prev = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + } + } else { + tcp_ack_now(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb); + } + } + return accepted_inseq; +} + +/** + * Parses the options contained in the incoming segment. (Code taken + * from uIP with only small changes.) + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u8_t c; + u8_t *opts, opt; + u16_t mss; + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + for(c = 0; c < (TCPH_HDRLEN(tcphdr) - 5) << 2 ;) { + opt = opts[c]; + if (opt == 0x00) { + /* End of options. */ + break; + } else if (opt == 0x01) { + ++c; + /* NOP option. */ + } else if (opt == 0x02 && + opts[c + 1] == 0x04) { + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + + /* And we are done processing options. */ + break; + } else { + if (opts[c + 1] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/lab/net/lwip/core/tcp_out.c b/lab/net/lwip/core/tcp_out.c new file mode 100644 index 0000000..c46e4b8 --- /dev/null +++ b/lab/net/lwip/core/tcp_out.c @@ -0,0 +1,953 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +/** + * Called by tcp_close() to send a segment including flags but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @param flags the flags to set in the segment header + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) +{ + /* no data, no length, flags, copy=1, no optdata, no optdatalen */ + return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, NULL, 0); +} + +/** + * Write data for sending (but does not send it immediately). + * + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block of the TCP connection to enqueue data for. + * @param data pointer to the data to send + * @param len length (in bytes) of the data to send + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + * + * @see tcp_write() + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags) +{ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", (void *)pcb, + data, len, (u16_t)apiflags)); + /* connection is in valid state for data transmission? */ + if (pcb->state == ESTABLISHED || + pcb->state == CLOSE_WAIT || + pcb->state == SYN_SENT || + pcb->state == SYN_RCVD) { + if (len > 0) { + return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, NULL, 0); + } + return ERR_OK; + } else { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | 3, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } +} + +/** + * Enqueue either data or TCP options (but not both) for tranmission + * + * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for. + * @param arg Pointer to the data to be enqueued for sending. + * @param len Data length in bytes + * @param flags tcp header flags to set in the outgoing segment + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @param optdata + * @param optlen + */ +err_t +tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, + u8_t flags, u8_t apiflags, + u8_t *optdata, u8_t optlen) +{ + struct pbuf *p; + struct tcp_seg *seg, *useg, *queue; + u32_t seqno; + u16_t left, seglen; + void *ptr; + u16_t queuelen; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags)); + LWIP_ERROR("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)", + ((len == 0) || (optlen == 0)), return ERR_ARG;); + LWIP_ERROR("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)", + ((arg == NULL) || (optdata == NULL)), return ERR_ARG;); + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + left = len; + ptr = arg; + + /* seqno will be the sequence number of the first segment enqueued + * by the call to this function. */ + seqno = pcb->snd_lbb; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + queuelen = pcb->snd_queuelen; + /* check for configured max queuelen and possible overflow */ + if ((queuelen >= TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + + /* First, break up the data into segments and tuck them together in + * the local "queue" variable. */ + useg = queue = seg = NULL; + seglen = 0; + while (queue == NULL || left > 0) { + + /* The segment length should be the MSS if the data to be enqueued + * is larger than the MSS. */ + seglen = left > pcb->mss? pcb->mss: left; + + /* Allocate memory for tcp_seg, and fill in fields. */ + seg = memp_malloc(MEMP_TCP_SEG); + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n")); + goto memerr; + } + seg->next = NULL; + seg->p = NULL; + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } + /* subsequent segments of to-be-queued data */ + else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("useg != NULL", useg != NULL); + useg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + useg = seg; + + /* If copy is set, memory should be allocated + * and data copied into pbuf, otherwise data comes from + * ROM or other static memory, and need not be copied. If + * optdata is != NULL, we have options instead of data. */ + + /* options? */ + if (optdata != NULL) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold optlen", + (seg->p->len >= optlen)); + queuelen += pbuf_clen(seg->p); + seg->dataptr = seg->p->payload; + } + /* copy from volatile memory? */ + else if (apiflags & TCP_WRITE_FLAG_COPY) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold the complete seglen", + (seg->p->len >= seglen)); + queuelen += pbuf_clen(seg->p); + if (arg != NULL) { + MEMCPY(seg->p->payload, ptr, seglen); + } + seg->dataptr = seg->p->payload; + } + /* do not copy data */ + else { + /* First, allocate a pbuf for holding the data. + * since the referenced data is available at least until it is sent out on the + * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM + * instead of PBUF_REF here. + */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } + ++queuelen; + /* reference the non-volatile payload data */ + p->payload = ptr; + seg->dataptr = ptr; + + /* Second, allocate a pbuf for the headers. */ + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n")); + goto memerr; + } + queuelen += pbuf_clen(seg->p); + + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(seg->p/*header*/, p/*data*/); + p = NULL; + } + + /* Now that there are more segments queued, we check again if the + length of the queue exceeds the configured maximum or overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + goto memerr; + } + + seg->len = seglen; + + /* build TCP header */ + if (pbuf_header(seg->p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + goto memerr; + } + seg->tcphdr = seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + seg->tcphdr->urgp = 0; + TCPH_FLAGS_SET(seg->tcphdr, flags); + /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ + + /* Copy the options into the header, if they are present. */ + if (optdata == NULL) { + TCPH_HDRLEN_SET(seg->tcphdr, 5); + } + else { + TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); + /* Copy options into data portion of segment. + Options can thus only be sent in non data carrying + segments such as SYN|ACK. */ + SMEMCPY(seg->dataptr, optdata, optlen); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + left -= seglen; + seqno += seglen; + ptr = (void *)((u8_t *)ptr + seglen); + } + + /* Now that the data to be enqueued has been broken up into TCP + segments in the queue variable, we add them to the end of the + pcb->unsent queue. */ + if (pcb->unsent == NULL) { + useg = NULL; + } + else { + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + } + /* { useg is last segment on the unsent queue, NULL if list is empty } */ + + /* If there is room in the last pbuf on the unsent queue, + chain the first pbuf on the queue together with that. */ + if (useg != NULL && + TCP_TCPLEN(useg) != 0 && + !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && + !(flags & (TCP_SYN | TCP_FIN)) && + /* fit within max seg size */ + useg->len + queue->len <= pcb->mss) { + /* Remove TCP header from first segment of our to-be-queued list */ + if(pbuf_header(queue->p, -TCP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + TCP_STATS_INC(tcp.err); + goto memerr; + } + pbuf_cat(useg->p, queue->p); + useg->len += queue->len; + useg->next = queue->next; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); + if (seg == queue) { + seg = NULL; + } + memp_free(MEMP_TCP_SEG, queue); + } + else { + /* empty list */ + if (useg == NULL) { + /* initialize list with this segment */ + pcb->unsent = queue; + } + /* enqueue segment */ + else { + useg->next = queue; + } + } + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + ++len; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + pcb->snd_lbb += len; + + pcb->snd_buf -= len; + + /* update number of segments on the queues */ + pcb->snd_queuelen = queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued, but only + if the segment has data (indicated by seglen > 0). */ + if (seg != NULL && seglen > 0 && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Find out what we can send and send it + * + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg, *useg; + u32_t wnd; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. */ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); + + seg = pcb->unsent; + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); + } + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt); + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), + IP_PROTO_TCP, p->tot_len); +#endif +#if LWIP_NETIF_HWADDRHINT + { + struct netif *netif; + netif = ip_route(&pcb->remote_ip); + if(netif != NULL){ + netif->addr_hint = &(pcb->addr_hint); + ip_output_if(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, + pcb->tos, IP_PROTO_TCP, netif); + netif->addr_hint = NULL; + } + } +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? */ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_enqueue had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_enqueue/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + tcp_output_segment(seg, pcb); + pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) { + pcb->snd_max = pcb->snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? */ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty? */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather at the head. We need to check for + * this case. -STJ Jul 27, 2004 */ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){ + /* add segment to head of unacked list */ + seg->next = pcb->unacked; + pcb->unacked = seg; + } else { + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } + + if (seg != NULL && pcb->persist_backoff == 0 && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { + /* prepare for persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + + pcb->flags &= ~TF_NAGLEMEMERR; + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + */ +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + struct netif *netif; + + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ip_addr_isany(&(pcb->local_ip))) { + netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + return; + } + ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); + } + + /* Set retransmission timer running if it is not currently enabled */ + if(pcb->rtime == -1) + pcb->rtime = 0; + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, + &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + { + struct netif *netif; + netif = ip_route(&pcb->remote_ip); + if(netif != NULL){ + netif->addr_hint = &(pcb->addr_hint); + ip_output_if(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, + pcb->tos, IP_PROTO_TCP, netif); + netif->addr_hint = NULL; + } + } +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst(u32_t seqno, u32_t ackno, + struct ip_addr *local_ip, struct ip_addr *remote_ip, + u16_t local_port, u16_t remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK); + tcphdr->wnd = htons(TCP_WND); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + /* Send output with hardcoded TTL since we have no access to the pcb */ + ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + seg = pcb->unacked->next; + pcb->unacked->next = pcb->unsent; + pcb->unsent = pcb->unacked; + pcb->unacked = seg; + + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + snmp_inc_tcpretranssegs(); + tcp_output(pcb); +} + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt - 1); + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, 0); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + { + struct netif *netif; + netif = ip_route(&pcb->remote_ip); + if(netif != NULL){ + netif->addr_hint = &(pcb->addr_hint); + ip_output_if(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, + 0, IP_PROTO_TCP, netif); + netif->addr_hint = NULL; + } + } +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" + U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) + seg = pcb->unsent; + + if(seg == NULL) + return; + + p = pbuf_alloc(PBUF_IP, TCP_HLEN + 1, PBUF_RAM); + + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seg->tcphdr->seqno; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, 0); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + /* Copy in one byte from the head of the unacked queue */ + *((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr; + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + { + struct netif *netif; + netif = ip_route(&pcb->remote_ip); + if(netif != NULL){ + netif->addr_hint = &(pcb->addr_hint); + ip_output_if(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, + 0, IP_PROTO_TCP, netif); + netif->addr_hint = NULL; + } + } +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/lab/net/lwip/core/udp.c b/lab/net/lwip/core/udp.c new file mode 100644 index 0000000..2f36c74 --- /dev/null +++ b/lab/net/lwip/core/udp.c @@ -0,0 +1,824 @@ +/** + * @file + * User Datagram Protocol module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB. + * @param inp network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + struct ip_hdr *iphdr; + u16_t src, dest; + u8_t local_match; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + iphdr = p->payload; + + /* Check minimum length (IP header + UDP header) + * and move payload pointer to UDP header */ + if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), + ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src))); + +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src */ + if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &(iphdr->src)))) { + pcb = inp->dhcp->pcb; + } + } + } + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, + ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), + ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if ((pcb->local_port == dest) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)) || +#if LWIP_IGMP + ip_addr_ismulticast(&(iphdr->dest)) || +#endif /* LWIP_IGMP */ + ip_addr_isbroadcast(&(iphdr->dest), inp))) { + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if LWIP_UDPLITE + if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ +#if CHECKSUM_CHECK_UDP + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } + if (inet_chksum_pseudo_partial(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { + LWIP_DEBUGF(UDP_DEBUG | 2, + ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } +#endif /* CHECKSUM_CHECK_UDP */ + } else +#endif /* LWIP_UDPLITE */ + { +#if CHECKSUM_CHECK_UDP + if (udphdr->chksum != 0) { + if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDP, p->tot_len) != 0) { + LWIP_DEBUGF(UDP_DEBUG | 2, + ("udp_input: UDP datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } +#endif /* CHECKSUM_CHECK_UDP */ + } + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + if (pcb != NULL) { + snmp_inc_udpindatagrams(); + /* callback */ + if (pcb->recv != NULL) { + /* now the recv function is responsible for freeing p */ + pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src); + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!ip_addr_isbroadcast(&iphdr->dest, inp) && + !ip_addr_ismulticast(&iphdr->dest)) { + /* move payload pointer back to ip header */ + pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); + LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); + icmp_dest_unreach(p, ICMP_DUR_PORT); + } +#endif /* LWIP_ICMP */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); +} + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *dst_ip, u16_t dst_port) +{ + struct netif *netif; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 3, ("udp_send\n")); + + /* find the outgoing network interface for this packet */ +#if LWIP_IGMP + netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); +#else + netif = ip_route(dst_ip); +#endif /* LWIP_IGMP */ + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | 1, ("udp_send: No route to 0x%"X32_F"\n", dst_ip->addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +} + +/** + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif) +{ + struct udp_hdr *udphdr; + struct ip_addr *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 2, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 2, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 2, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* PCB local address is IP_ANY_ADDR? */ + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, + IP_PROTO_UDPLITE, q->tot_len, chklen); + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) + udphdr->chksum = 0xffff; +#endif /* CHECKSUM_CHECK_UDP */ + /* output to IP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + udphdr->chksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_CHECK_UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); + /* output to IP */ +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 3, ("udp_bind(ipaddr = ")); + ip_addr_debug_print(UDP_DEBUG, ipaddr); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 3, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + + /* this code does not allow upper layer to share a UDP port for + listening to broadcast or multicast traffic (See SO_REUSE_ADDR and + SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR + combine with implementation of UDP PCB flags. Leon Woestenberg. */ +#ifdef LWIP_UDP_TODO + /* port matches that of PCB in list? */ + else + if ((ipcb->local_port == port) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ip_addr_isany(&(ipcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } +#endif + } + + ip_addr_set(&pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { +#ifndef UDP_LOCAL_PORT_RANGE_START +#define UDP_LOCAL_PORT_RANGE_START 4096 +#define UDP_LOCAL_PORT_RANGE_END 0x7fff +#endif + port = UDP_LOCAL_PORT_RANGE_START; + ipcb = udp_pcbs; + while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == port) { + /* port is already used by another udp_pcb */ + port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + if (ipcb != NULL) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", + (u16_t)(ntohl(pcb->local_ip.addr) >> 24 & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) >> 16 & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) >> 8 & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port)); + return ERR_OK; +} +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t +udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) + return err; + } + + ip_addr_set(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { + struct netif *netif; + + if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + pcb->local_ip = netif->ip_addr; + } else if (ip_addr_isany(&pcb->remote_ip)) { + pcb->local_ip.addr = 0; + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", + (u16_t)(ntohl(pcb->remote_ip.addr) >> 24 & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) >> 16 & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) >> 8 & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) & 0xff), pcb->remote_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ip_addr_set(&pcb->remote_ip, IP_ADDR_ANY); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void +udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port), + void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + return pcb; +} + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/lab/net/lwip/include/ipv4/lwip/autoip.h b/lab/net/lwip/include/ipv4/lwip/autoip.h new file mode 100644 index 0000000..076a2ed --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/autoip.h @@ -0,0 +1,105 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +#ifndef __LWIP_AUTOIP_H__ +#define __LWIP_AUTOIP_H__ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +/* AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +#define AUTOIP_STATE_OFF 0 +#define AUTOIP_STATE_PROBING 1 +#define AUTOIP_STATE_ANNOUNCING 2 +#define AUTOIP_STATE_BOUND 3 + +struct autoip +{ + struct ip_addr llipaddr; /* the currently selected, probed, announced or used LL IP-Address */ + u8_t state; /* current AutoIP state machine state */ + u8_t sent_num; /* sent number of probes or announces, dependent on state */ + u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u8_t lastconflict; /* ticks until a conflict can be solved by defending */ + u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */ +}; + + +/** Init srand, has to be called before entering mainloop */ +void autoip_init(void); + +/** Start AutoIP client */ +err_t autoip_start(struct netif *netif); + +/** Stop AutoIP client */ +err_t autoip_stop(struct netif *netif); + +/** Handles every incoming ARP Packet, called by etharp_arp_input */ +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); + +/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */ +void autoip_tmr(void); + +#endif /* LWIP_AUTOIP */ + +#endif /* __LWIP_AUTOIP_H__ */ diff --git a/lab/net/lwip/include/ipv4/lwip/icmp.h b/lab/net/lwip/include/ipv4/lwip/icmp.h new file mode 100644 index 0000000..59a31f9 --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/icmp.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ICMP_H__ +#define __LWIP_ICMP_H__ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ +#define ICMP_ECHO 8 /* echo */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ +#define ICMP_TSR 14 /* timestamp reply */ +#define ICMP_IRQ 15 /* information request */ +#define ICMP_IR 16 /* information reply */ + +enum icmp_dur_type { + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_PROTO = 2, /* protocol unreachable */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ +}; + +enum icmp_te_type { + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ +}; + +void icmp_input(struct pbuf *p, struct netif *inp); + +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FIELD(u16_t _type_code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +PACK_STRUCT_BEGIN +struct icmp_dur_hdr { + PACK_STRUCT_FIELD(u16_t _type_code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t unused); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +PACK_STRUCT_BEGIN +struct icmp_te_hdr { + PACK_STRUCT_FIELD(u16_t _type_code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t unused); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define ICMPH_TYPE(hdr) (ntohs((hdr)->_type_code) >> 8) +#define ICMPH_CODE(hdr) (ntohs((hdr)->_type_code) & 0xff) + +#define ICMPH_TYPE_SET(hdr, type) ((hdr)->_type_code = htons(ICMPH_CODE(hdr) | ((type) << 8))) +#define ICMPH_CODE_SET(hdr, code) ((hdr)->_type_code = htons((code) | (ICMPH_TYPE(hdr) << 8))) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ICMP */ + +#endif /* __LWIP_ICMP_H__ */ diff --git a/lab/net/lwip/include/ipv4/lwip/igmp.h b/lab/net/lwip/include/ipv4/lwip/igmp.h new file mode 100644 index 0000000..59c933f --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/igmp.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef __LWIP_IGMP_H__ +#define __LWIP_IGMP_H__ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IGMP constants + */ +#define IP_PROTO_IGMP 2 +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404 +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* MAC Filter Actions */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/* + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(struct ip_addr igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * now a group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ + +struct igmp_group { + struct igmp_group *next; + struct netif *interface; + struct ip_addr group_address; + u8_t last_reporter_flag; /* signifies we were the last person to report */ + u8_t group_state; + u16_t timer; + u8_t use; /* counter of simultaneous uses */ +}; + + +/* Prototypes */ +void igmp_init(void); + +err_t igmp_start( struct netif *netif); + +err_t igmp_stop( struct netif *netif); + +void igmp_report_groups( struct netif *netif); + +struct igmp_group *igmp_lookfor_group( struct netif *ifp, struct ip_addr *addr); + +struct igmp_group *igmp_lookup_group( struct netif *ifp, struct ip_addr *addr); + +err_t igmp_remove_group( struct igmp_group *group); + +void igmp_input( struct pbuf *p, struct netif *inp, struct ip_addr *dest); + +err_t igmp_joingroup( struct ip_addr *ifaddr, struct ip_addr *groupaddr); + +err_t igmp_leavegroup( struct ip_addr *ifaddr, struct ip_addr *groupaddr); + +void igmp_tmr(void); + +void igmp_timeout( struct igmp_group *group); + +void igmp_start_timer( struct igmp_group *group, u8_t max_time); + +void igmp_stop_timer( struct igmp_group *group); + +void igmp_delaying_member( struct igmp_group *group, u8_t maxresp); + +err_t igmp_ip_output_if( struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t proto, struct netif *netif); + +void igmp_send( struct igmp_group *group, u8_t type); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/lab/net/lwip/include/ipv4/lwip/inet.h b/lab/net/lwip/include/ipv4/lwip/inet.h new file mode 100644 index 0000000..24b75e1 --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/inet.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_H__ +#define __LWIP_INET_H__ + +#include "lwip/opt.h" + +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; + +u32_t inet_addr(const char *cp); +int inet_aton(const char *cp, struct in_addr *addr); +char *inet_ntoa(struct in_addr addr); /* returns ptr to static buffer; not reentrant! */ + +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ + +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define htons(x) (x) +#define ntohs(x) (x) +#define htonl(x) (x) +#define ntohl(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#ifdef LWIP_PREFIX_BYTEORDER_FUNCS +/* workaround for naming collisions on some platforms */ +#define htons lwip_htons +#define ntohs lwip_ntohs +#define htonl lwip_htonl +#define ntohl lwip_ntohl +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ +#if LWIP_PLATFORM_BYTESWAP +#define htons(x) LWIP_PLATFORM_HTONS(x) +#define ntohs(x) LWIP_PLATFORM_HTONS(x) +#define htonl(x) LWIP_PLATFORM_HTONL(x) +#define ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t htons(u16_t x); +u16_t ntohs(u16_t x); +u32_t htonl(u32_t x); +u32_t ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ diff --git a/lab/net/lwip/include/ipv4/lwip/inet_chksum.h b/lab/net/lwip/include/ipv4/lwip/inet_chksum.h new file mode 100644 index 0000000..d7c7492 --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/inet_chksum.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/lab/net/lwip/include/ipv4/lwip/ip.h b/lab/net/lwip/include/ipv4/lwip/ip.h new file mode 100644 index 0000000..3b760f0 --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/ip.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ip_init() /* Compatibility define, not init needed. */ +struct netif *ip_route(struct ip_addr *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif); + +#define IP_HLEN 20 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + /* ip addresses in network byte order */ \ + struct ip_addr local_ip; \ + struct ip_addr remote_ip; \ + /* Socket options */ \ + u16_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +#define SOF_DEBUG (u16_t)0x0001U /* turn on debugging info recording */ +#define SOF_ACCEPTCONN (u16_t)0x0002U /* socket has had listen() */ +#define SOF_REUSEADDR (u16_t)0x0004U /* allow local address reuse */ +#define SOF_KEEPALIVE (u16_t)0x0008U /* keep connections alive */ +#define SOF_DONTROUTE (u16_t)0x0010U /* just use interface addresses */ +#define SOF_BROADCAST (u16_t)0x0020U /* permit sending of broadcast msgs */ +#define SOF_USELOOPBACK (u16_t)0x0040U /* bypass hardware when possible */ +#define SOF_LINGER (u16_t)0x0080U /* linger on close if data present */ +#define SOF_OOBINLINE (u16_t)0x0100U /* leave received OOB data in line */ +#define SOF_REUSEPORT (u16_t)0x0200U /* allow local address & port reuse */ + + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_hdr { + /* version / header length / type of service */ + PACK_STRUCT_FIELD(u16_t _v_hl_tos); + /* total length */ + PACK_STRUCT_FIELD(u16_t _len); + /* identification */ + PACK_STRUCT_FIELD(u16_t _id); + /* fragment offset field */ + PACK_STRUCT_FIELD(u16_t _offset); +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + /* time to live / protocol*/ + PACK_STRUCT_FIELD(u16_t _ttl_proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(struct ip_addr src); + PACK_STRUCT_FIELD(struct ip_addr dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12) +#define IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f) +#define IPH_TOS(hdr) (ntohs((hdr)->_v_hl_tos) & 0xff) +#define IPH_LEN(hdr) ((hdr)->_len) +#define IPH_ID(hdr) ((hdr)->_id) +#define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_TTL(hdr) (ntohs((hdr)->_ttl_proto) >> 8) +#define IPH_PROTO(hdr) (ntohs((hdr)->_ttl_proto) & 0xff) +#define IPH_CHKSUM(hdr) ((hdr)->_chksum) + +#define IPH_VHLTOS_SET(hdr, v, hl, tos) (hdr)->_v_hl_tos = (htons(((v) << 12) | ((hl) << 8) | (tos))) +#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl_proto = (htons(IPH_PROTO(hdr) | ((u16_t)(ttl) << 8))) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (IPH_TTL(hdr) << 8))) +#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/lab/net/lwip/include/ipv4/lwip/ip_addr.h b/lab/net/lwip/include/ipv4/lwip/ip_addr.h new file mode 100644 index 0000000..12e7eb0 --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/ip_addr.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +struct netif; + +extern const struct ip_addr ip_addr_any; +extern const struct ip_addr ip_addr_broadcast; + +/** IP_ADDR_ can be used as a fixed IP address + * for the wildcard and the broadcast address + */ +#define IP_ADDR_ANY ((struct ip_addr *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((struct ip_addr *)&ip_addr_broadcast) + +#define INADDR_NONE ((u32_t)0xffffffffUL) /* 255.255.255.255 */ +#define INADDR_LOOPBACK ((u32_t)0x7f000001UL) /* 127.0.0.1 */ + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ + +#define IN_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) + +#define IN_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IN_MULTICAST(a) IN_CLASSD(a) + +#define IN_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IN_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IN_LOOPBACKNET 127 /* official! */ + +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = htonl(((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff)) + +#define ip_addr_set(dest, src) (dest)->addr = \ + ((src) == NULL? 0:\ + (src)->addr) +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == 0) + +u8_t ip_addr_isbroadcast(struct ip_addr *, struct netif *); + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & ntohl(0xf0000000UL)) == ntohl(0xe0000000UL)) + +#define ip_addr_islinklocal(addr1) (((addr1)->addr & ntohl(0xffff0000UL)) == ntohl(0xa9fe0000UL)) + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr ? (u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff : 0, \ + ipaddr ? (u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff : 0, \ + ipaddr ? (u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff : 0, \ + ipaddr ? (u16_t)ntohl((ipaddr)->addr) & 0xff : 0)) + +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff) +#define ip4_addr2(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff) +#define ip4_addr3(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff) +#define ip4_addr4(ipaddr) ((u16_t)(ntohl((ipaddr)->addr)) & 0xff) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/lab/net/lwip/include/ipv4/lwip/ip_frag.h b/lab/net/lwip/include/ipv4/lwip/ip_frag.h new file mode 100644 index 0000000..380e604 --- /dev/null +++ b/lab/net/lwip/include/ipv4/lwip/ip_frag.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef __LWIP_IP_FRAG_H__ +#define __LWIP_IP_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +err_t ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/lab/net/lwip/include/ipv6/lwip/icmp.h b/lab/net/lwip/include/ipv6/lwip/icmp.h new file mode 100644 index 0000000..87e9ffd --- /dev/null +++ b/lab/net/lwip/include/ipv6/lwip/icmp.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ICMP_H__ +#define __LWIP_ICMP_H__ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP6_DUR 1 +#define ICMP6_TE 3 +#define ICMP6_ECHO 128 /* echo */ +#define ICMP6_ER 129 /* echo reply */ + + +enum icmp_dur_type { + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_PROTO = 2, /* protocol unreachable */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ +}; + +enum icmp_te_type { + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ +}; + +void icmp_input(struct pbuf *p, struct netif *inp); + +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +struct icmp_echo_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u16_t id; + u16_t seqno; +}; + +struct icmp_dur_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u32_t unused; +}; + +struct icmp_te_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u32_t unused; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ICMP */ + +#endif /* __LWIP_ICMP_H__ */ + diff --git a/lab/net/lwip/include/ipv6/lwip/inet.h b/lab/net/lwip/include/ipv6/lwip/inet.h new file mode 100644 index 0000000..de1a0b6 --- /dev/null +++ b/lab/net/lwip/include/ipv6/lwip/inet.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_H__ +#define __LWIP_INET_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *data, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len); + +u32_t inet_addr(const char *cp); +s8_t inet_aton(const char *cp, struct in_addr *addr); + +#ifndef _MACHINE_ENDIAN_H_ +#ifndef _NETINET_IN_H +#ifndef _LINUX_BYTEORDER_GENERIC_H +u16_t htons(u16_t n); +u16_t ntohs(u16_t n); +u32_t htonl(u32_t n); +u32_t ntohl(u32_t n); +#endif /* _LINUX_BYTEORDER_GENERIC_H */ +#endif /* _NETINET_IN_H */ +#endif /* _MACHINE_ENDIAN_H_ */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/lab/net/lwip/include/ipv6/lwip/ip.h b/lab/net/lwip/include/ipv6/lwip/ip.h new file mode 100644 index 0000000..f6e59cc --- /dev/null +++ b/lab/net/lwip/include/ipv6/lwip/ip.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP_HLEN 40 + +#define IP_PROTO_ICMP 58 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB struct ip_addr local_ip; \ + struct ip_addr remote_ip; \ + /* Socket options */ \ + u16_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl; \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + + +/* The IPv6 header. */ +struct ip_hdr { +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t tclass1:4, v:4; + u8_t flow1:4, tclass2:4; +#else + u8_t v:4, tclass1:4; + u8_t tclass2:8, flow1:4; +#endif + u16_t flow2; + u16_t len; /* payload length */ + u8_t nexthdr; /* next header */ + u8_t hoplim; /* hop limit (TTL) */ + struct ip_addr src, dest; /* source and destination IP addresses */ +}; + +#define IPH_PROTO(hdr) (iphdr->nexthdr) + +void ip_init(void); + +#include "lwip/netif.h" + +struct netif *ip_route(struct ip_addr *dest); + +void ip_input(struct pbuf *p, struct netif *inp); + +/* source and destination addresses in network byte order, please */ +err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto); + +err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto, + struct netif *netif); + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/lab/net/lwip/include/ipv6/lwip/ip_addr.h b/lab/net/lwip/include/ipv6/lwip/ip_addr.h new file mode 100644 index 0000000..b2d8ae5 --- /dev/null +++ b/lab/net/lwip/include/ipv6/lwip/ip_addr.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP_ADDR_ANY 0 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN + struct ip_addr { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6_ADDR(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = htonl((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \ + (ipaddr)->addr[1] = htonl(((c & 0xffff) << 16) | (d & 0xffff)); \ + (ipaddr)->addr[2] = htonl(((e & 0xffff) << 16) | (f & 0xffff)); \ + (ipaddr)->addr[3] = htonl(((g & 0xffff) << 16) | (h & 0xffff)); } while(0) + +u8_t ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, + struct ip_addr *mask); +u8_t ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2); +void ip_addr_set(struct ip_addr *dest, struct ip_addr *src); +u8_t ip_addr_isany(struct ip_addr *addr); + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F"\n", \ + (ntohl(ipaddr->addr[0]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[0]) & 0xffff, \ + (ntohl(ipaddr->addr[1]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[1]) & 0xffff, \ + (ntohl(ipaddr->addr[2]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[2]) & 0xffff, \ + (ntohl(ipaddr->addr[3]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[3]) & 0xffff)); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/lab/net/lwip/include/lwip/api.h b/lab/net/lwip/include/lwip/api.h new file mode 100644 index 0000000..31d767f --- /dev/null +++ b/lab/net/lwip/include/lwip/api.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_H__ +#define __LWIP_API_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) (t&0xF0) +#define NETCONNTYPE_DATAGRAM(t) (t&0xE0) + +enum netconn_type { + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM= 0x22, + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 +}; + +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS +}; + +#if LWIP_IGMP +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP */ + +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ +struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ + enum netconn_type type; + /** current state of the netconn */ + enum netconn_state state; + /** the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + /** the last error this netconn had */ + err_t err; + /** sem that is used to synchroneously execute functions in the core context */ + sys_sem_t op_completed; + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ + sys_mbox_t recvmbox; + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; + /** only used for socket layer */ + int socket; +#if LWIP_SO_RCVTIMEO + /** timeout to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox */ + int recv_bufsize; +#endif /* LWIP_SO_RCVBUF */ + u16_t recv_avail; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. */ + struct api_msg_msg *write_msg; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + int write_offset; +#if LWIP_TCPIP_CORE_LOCKING + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores whether to wake up the original application task + if data couldn't be sent in the first try. */ + u8_t write_delayed; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; +}; + +/* Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } + +/* Network connection functions: */ +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) +struct +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +err_t netconn_delete (struct netconn *conn); +enum netconn_type netconn_type (struct netconn *conn); + +err_t netconn_getaddr (struct netconn *conn, + struct ip_addr *addr, + u16_t *port, + u8_t local); +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + +err_t netconn_bind (struct netconn *conn, + struct ip_addr *addr, + u16_t port); +err_t netconn_connect (struct netconn *conn, + struct ip_addr *addr, + u16_t port); +err_t netconn_disconnect (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) +struct netconn * netconn_accept (struct netconn *conn); +struct netbuf * netconn_recv (struct netconn *conn); +err_t netconn_sendto (struct netconn *conn, + struct netbuf *buf, struct ip_addr *addr, u16_t port); +err_t netconn_send (struct netconn *conn, + struct netbuf *buf); +err_t netconn_write (struct netconn *conn, + const void *dataptr, int size, + u8_t apiflags); +err_t netconn_close (struct netconn *conn); + +#if LWIP_IGMP +err_t netconn_join_leave_group (struct netconn *conn, + struct ip_addr *multiaddr, + struct ip_addr *interface, + enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, struct ip_addr *addr); +#endif /* LWIP_DNS */ + +#define netconn_err(conn) ((conn)->err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_H__ */ diff --git a/lab/net/lwip/include/lwip/api_msg.h b/lab/net/lwip/include/lwip/api_msg.h new file mode 100644 index 0000000..4c9b48e --- /dev/null +++ b/lab/net/lwip/include/lwip/api_msg.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_MSG_H__ +#define __LWIP_API_MSG_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ +struct api_msg_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ + struct netconn *conn; + /** Depending on the executed function, one of these union members is used */ + union { + /** used for do_send */ + struct netbuf *b; + /** used for do_newconn */ + struct { + u8_t proto; + } n; + /** used for do_bind and do_connect */ + struct { + struct ip_addr *ipaddr; + u16_t port; + } bc; + /** used for do_getaddr */ + struct { + struct ip_addr *ipaddr; + u16_t *port; + u8_t local; + } ad; + /** used for do_write */ + struct { + const void *dataptr; + int len; + u8_t apiflags; + } w; + /** used ofr do_recv */ + struct { + u16_t len; + } r; +#if LWIP_IGMP + /** used for do_join_leave_group */ + struct { + struct ip_addr *multiaddr; + struct ip_addr *interface; + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +}; + +/** This struct contains a function to execute in another thread context and + a struct api_msg_msg that serves as an argument for this function. + This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ +struct api_msg { + /** function to execute in tcpip_thread context */ + void (* function)(struct api_msg_msg *msg); + /** arguments for this function */ + struct api_msg_msg msg; +}; + +#if LWIP_DNS +/** As do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ + const char *name; + /** Rhe resolved address is stored here */ + struct ip_addr *addr; + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t sem; + /** Errors are given back here */ + err_t *err; +}; +#endif /* LWIP_DNS */ + +void do_newconn ( struct api_msg_msg *msg); +void do_delconn ( struct api_msg_msg *msg); +void do_bind ( struct api_msg_msg *msg); +void do_connect ( struct api_msg_msg *msg); +void do_disconnect ( struct api_msg_msg *msg); +void do_listen ( struct api_msg_msg *msg); +void do_send ( struct api_msg_msg *msg); +void do_recv ( struct api_msg_msg *msg); +void do_write ( struct api_msg_msg *msg); +void do_getaddr ( struct api_msg_msg *msg); +void do_close ( struct api_msg_msg *msg); +#if LWIP_IGMP +void do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +void do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_MSG_H__ */ diff --git a/lab/net/lwip/include/lwip/arch.h b/lab/net/lwip/include/lwip/arch.h new file mode 100644 index 0000000..7d1400b --- /dev/null +++ b/lab/net/lwip/include/lwip/arch.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ARCH_H__ +#define __LWIP_ARCH_H__ + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +#include "arch/cc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PACK_STRUCT_BEGIN +#define PACK_STRUCT_BEGIN +#endif /* PACK_STRUCT_BEGIN */ + +#ifndef PACK_STRUCT_END +#define PACK_STRUCT_END +#endif /* PACK_STRUCT_END */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + + +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + + +#define ENSROK 0 /* DNS server returned answer with no data */ +#define ENSRNODATA 160 /* DNS server returned answer with no data */ +#define ENSRFORMERR 161 /* DNS server claims query was misformatted */ +#define ENSRSERVFAIL 162 /* DNS server returned general failure */ +#define ENSRNOTFOUND 163 /* Domain name not found */ +#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */ +#define ENSRREFUSED 165 /* DNS server refused query */ +#define ENSRBADQUERY 166 /* Misformatted DNS query */ +#define ENSRBADNAME 167 /* Misformatted domain name */ +#define ENSRBADFAMILY 168 /* Unsupported address family */ +#define ENSRBADRESP 169 /* Misformatted DNS reply */ +#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */ +#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */ +#define ENSROF 172 /* End of file */ +#define ENSRFILE 173 /* Error reading file */ +#define ENSRNOMEM 174 /* Out of memory */ +#define ENSRDESTRUCTION 175 /* Application terminated lookup */ +#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */ +#define ENSRCNAMELOOP 177 /* Domain name is too long */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ARCH_H__ */ diff --git a/lab/net/lwip/include/lwip/debug.h b/lab/net/lwip/include/lwip/debug.h new file mode 100644 index 0000000..d5c4e47 --- /dev/null +++ b/lab/net/lwip/include/lwip/debug.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEBUG_H__ +#define __LWIP_DEBUG_H__ + +#include "lwip/arch.h" + +/** lower two bits indicate debug level + * - 0 off + * - 1 warning + * - 2 serious + * - 3 severe + */ +#define LWIP_DBG_LEVEL_OFF 0x00 +#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +#define LWIP_DBG_MASK_LEVEL 0x03 + +/** flag for LWIP_DEBUGF to enable that debug message */ +#define LWIP_DBG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define LWIP_DBG_OFF 0x00U + +/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ +#define LWIP_DBG_TRACE 0x40U +/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ +#define LWIP_DBG_STATE 0x20U +/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ +#define LWIP_DBG_FRESH 0x10U +/** flag for LWIP_DEBUGF to halt after printing this debug message */ +#define LWIP_DBG_HALT 0x08U + +#ifndef LWIP_NOASSERT +#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ + LWIP_PLATFORM_ASSERT(message); } while(0) +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +/** if "expression" isn't true, then print "message" and execute "handler" expression */ +#ifndef LWIP_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ASSERT(message); handler;}} while(0) +#endif /* LWIP_ERROR */ + +#ifdef LWIP_DEBUG +/** print debug message only if debug message type is enabled... + * AND is of correct type AND is at least LWIP_DBG_LEVEL + */ +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) +#endif /* LWIP_DEBUG */ + +#endif /* __LWIP_DEBUG_H__ */ + diff --git a/lab/net/lwip/include/lwip/def.h b/lab/net/lwip/include/lwip/def.h new file mode 100644 index 0000000..d2ed251 --- /dev/null +++ b/lab/net/lwip/include/lwip/def.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEF_H__ +#define __LWIP_DEF_H__ + +/* this might define NULL already */ +#include "lwip/arch.h" + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + + +#endif /* __LWIP_DEF_H__ */ + diff --git a/lab/net/lwip/include/lwip/dhcp.h b/lab/net/lwip/include/lwip/dhcp.h new file mode 100644 index 0000000..400f81f --- /dev/null +++ b/lab/net/lwip/include/lwip/dhcp.h @@ -0,0 +1,246 @@ +/** @file + */ + +#ifndef __LWIP_DHCP_H__ +#define __LWIP_DHCP_H__ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** period (in seconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_SECS 60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS*1000) +/** period (in milliseconds) of the application calling dhcp_fine_tmr() */ +#define DHCP_FINE_TIMER_MSECS 500 + +struct dhcp +{ + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; + /** transaction identifier of last sent request */ + u32_t xid; + /** our connection to the DHCP server */ + struct udp_pcb *pcb; + /** (first) pbuf of incoming msg */ + struct pbuf *p; + /** incoming msg */ + struct dhcp_msg *msg_in; + /** incoming msg options */ + struct dhcp_msg *options_in; + /** ingoing msg options length */ + u16_t options_in_len; + + struct pbuf *p_out; /* pbuf of outcoming msg */ + struct dhcp_msg *msg_out; /* outgoing msg */ + u16_t options_out_len; /* outgoing msg options length */ + u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ + u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + struct ip_addr server_ip_addr; /* dhcp server address that offered this lease */ + struct ip_addr offered_ip_addr; + struct ip_addr offered_sn_mask; + struct ip_addr offered_gw_addr; + struct ip_addr offered_bc_addr; +#define DHCP_MAX_DNS 2 + u32_t dns_count; /* actual number of DNS servers obtained */ + struct ip_addr offered_dns_addr[DHCP_MAX_DNS]; /* DNS server addresses */ + + u32_t offered_t0_lease; /* lease period (in seconds) */ + u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ + u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */ +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif +/** Patch #1308 + * TODO: See dhcp.c "TODO"s + */ +#if 0 + struct ip_addr offered_si_addr; + u8_t *boot_file_name; +#endif +}; + +/* MUST be compiled with "pack structs" or equivalent! */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** minimum set of fields of any DHCP message */ +struct dhcp_msg +{ + PACK_STRUCT_FIELD(u8_t op); + PACK_STRUCT_FIELD(u8_t htype); + PACK_STRUCT_FIELD(u8_t hlen); + PACK_STRUCT_FIELD(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(struct ip_addr ciaddr); + PACK_STRUCT_FIELD(struct ip_addr yiaddr); + PACK_STRUCT_FIELD(struct ip_addr siaddr); + PACK_STRUCT_FIELD(struct ip_addr giaddr); +#define DHCP_CHADDR_LEN 16U + PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); +#define DHCP_SNAME_LEN 64U + PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); +#define DHCP_FILE_LEN 128U + PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]); + PACK_STRUCT_FIELD(u32_t cookie); +#define DHCP_MIN_OPTIONS_LEN 68U +/** make sure user does not configure this too small */ +#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN)) +# undef DHCP_OPTIONS_LEN +#endif +/** allow this to be configured in lwipopts.h, but not too small */ +#if (!defined(DHCP_OPTIONS_LEN)) +/** set this to be sufficient for your options in outgoing DHCP msgs */ +# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN +#endif + PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** start DHCP configuration */ +err_t dhcp_start(struct netif *netif); +/** enforce early lease renewal (not needed normally)*/ +err_t dhcp_renew(struct netif *netif); +/** release the DHCP lease, usually called before dhcp_stop()*/ +err_t dhcp_release(struct netif *netif); +/** stop DHCP configuration */ +void dhcp_stop(struct netif *netif); +/** inform server of our manual IP address */ +void dhcp_inform(struct netif *netif); + +/** if enabled, check whether the offered IP address is not in use, using ARP */ +#if DHCP_DOES_ARP_CHECK +void dhcp_arp_reply(struct netif *netif, struct ip_addr *addr); +#endif + +/** to be called every minute */ +void dhcp_coarse_tmr(void); +/** to be called every half second */ +void dhcp_fine_tmr(void); + +/** DHCP message item offsets and length */ +#define DHCP_MSG_OFS (UDP_DATA_OFS) + #define DHCP_OP_OFS (DHCP_MSG_OFS + 0) + #define DHCP_HTYPE_OFS (DHCP_MSG_OFS + 1) + #define DHCP_HLEN_OFS (DHCP_MSG_OFS + 2) + #define DHCP_HOPS_OFS (DHCP_MSG_OFS + 3) + #define DHCP_XID_OFS (DHCP_MSG_OFS + 4) + #define DHCP_SECS_OFS (DHCP_MSG_OFS + 8) + #define DHCP_FLAGS_OFS (DHCP_MSG_OFS + 10) + #define DHCP_CIADDR_OFS (DHCP_MSG_OFS + 12) + #define DHCP_YIADDR_OFS (DHCP_MSG_OFS + 16) + #define DHCP_SIADDR_OFS (DHCP_MSG_OFS + 20) + #define DHCP_GIADDR_OFS (DHCP_MSG_OFS + 24) + #define DHCP_CHADDR_OFS (DHCP_MSG_OFS + 28) + #define DHCP_SNAME_OFS (DHCP_MSG_OFS + 44) + #define DHCP_FILE_OFS (DHCP_MSG_OFS + 108) +#define DHCP_MSG_LEN 236 + +#define DHCP_COOKIE_OFS (DHCP_MSG_OFS + DHCP_MSG_LEN) +#define DHCP_OPTIONS_OFS (DHCP_MSG_OFS + DHCP_MSG_LEN + 4) + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +/** DHCP client states */ +#define DHCP_REQUESTING 1 +#define DHCP_INIT 2 +#define DHCP_REBOOTING 3 +#define DHCP_REBINDING 4 +#define DHCP_RENEWING 5 +#define DHCP_SELECTING 6 +#define DHCP_INFORMING 7 +#define DHCP_CHECKING 8 +#define DHCP_PERMANENT 9 +#define DHCP_BOUND 10 +/** not yet implemented #define DHCP_RELEASING 11 */ +#define DHCP_BACKING_OFF 12 +#define DHCP_OFF 13 + +/** AUTOIP cooperatation flags */ +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +#define DHCP_HTYPE_ETH 1 + +#define DHCP_HLEN_ETH 6 + +#define DHCP_BROADCAST_FLAG 15 +#define DHCP_BROADCAST_MASK (1 << DHCP_FLAG_BROADCAST) + +/** BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_END 255 + +/** DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/** possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + +#endif /*__LWIP_DHCP_H__*/ diff --git a/lab/net/lwip/include/lwip/dns.h b/lab/net/lwip/include/lwip/dns.h new file mode 100644 index 0000000..6196742 --- /dev/null +++ b/lab/net/lwip/include/lwip/dns.h @@ -0,0 +1,92 @@ +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ + +/** DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to a struct ip_addr containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, struct ip_addr *ipaddr, void *callback_arg); + + +void dns_init(void); + +void dns_tmr(void); + +void dns_setserver(u8_t numdns, struct ip_addr *dnsserver); + +struct ip_addr dns_getserver(u8_t numdns); + +err_t dns_gethostbyname(const char *hostname, struct ip_addr *addr, + dns_found_callback found, void *callback_arg); + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/lab/net/lwip/include/lwip/err.h b/lab/net/lwip/include/lwip/err.h new file mode 100644 index 0000000..6967644 --- /dev/null +++ b/lab/net/lwip/include/lwip/err.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ERR_H__ +#define __LWIP_ERR_H__ + +#include "lwip/opt.h" +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ + typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ + +/* Definitions for error constants. */ + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ +#define ERR_RTE -4 /* Routing problem. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_RTE) + +#define ERR_ABRT -5 /* Connection aborted. */ +#define ERR_RST -6 /* Connection reset. */ +#define ERR_CLSD -7 /* Connection closed. */ +#define ERR_CONN -8 /* Not connected. */ + +#define ERR_VAL -9 /* Illegal value. */ + +#define ERR_ARG -10 /* Illegal argument. */ + +#define ERR_USE -11 /* Address in use. */ + +#define ERR_IF -12 /* Low-level netif error */ +#define ERR_ISCONN -13 /* Already connected. */ + +#define ERR_INPROGRESS -14 /* Operation in progress */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/lab/net/lwip/include/lwip/init.h b/lab/net/lwip/include/lwip/init.h new file mode 100644 index 0000000..c0869cf --- /dev/null +++ b/lab/net/lwip/include/lwip/init.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INIT_H__ +#define __LWIP_INIT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Modules initialization */ +void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/lab/net/lwip/include/lwip/mem.h b/lab/net/lwip/include/lwip/mem.h new file mode 100644 index 0000000..ff9521f --- /dev/null +++ b/lab/net/lwip/include/lwip/mem.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_MEM_H__ +#define __LWIP_MEM_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; + +/* aliases for C library malloc() */ +#define mem_init() +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_free +#define mem_free free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#endif +#ifndef mem_realloc +#define mem_realloc realloc +#endif +#else /* MEM_LIBC_MALLOC */ + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ +#if MEM_SIZE > 64000l +typedef u32_t mem_size_t; +#else +typedef u16_t mem_size_t; +#endif /* MEM_SIZE > 64000 */ + +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_realloc is not used when using pools instead of a heap: + we can't free part of a pool element and don't want to copy the rest */ +#define mem_realloc(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void); +void *mem_realloc(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#endif /* MEM_LIBC_MALLOC */ + +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#endif + +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEM_H__ */ diff --git a/lab/net/lwip/include/lwip/memp.h b/lab/net/lwip/include/lwip/memp.h new file mode 100644 index 0000000..a043863 --- /dev/null +++ b/lab/net/lwip/include/lwip/memp.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_MEMP_H__ +#define __LWIP_MEMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, +#include "lwip/memp_std.h" + MEMP_MAX +} memp_t; + +#if MEM_USE_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) + +extern const u16_t memp_sizes[MEMP_MAX]; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/lab/net/lwip/include/lwip/memp_std.h b/lab/net/lwip/include/lwip/memp_std.h new file mode 100644 index 0000000..c314bf8 --- /dev/null +++ b/lab/net/lwip/include/lwip/memp_std.h @@ -0,0 +1,101 @@ +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, size, "MALLOC_"#size) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ + +#if LWIP_NETCONN +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* NO_SYS==0 */ + +#if ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if NO_SYS==0 +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* NO_SYS==0 */ + + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/lab/net/lwip/include/lwip/netbuf.h b/lab/net/lwip/include/lwip/netbuf.h new file mode 100644 index 0000000..f6de3a4 --- /dev/null +++ b/lab/net/lwip/include/lwip/netbuf.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netbuf { + struct pbuf *p, *ptr; + struct ip_addr *addr; + u16_t port; +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +u16_t netbuf_len (struct netbuf *buf); +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) ((buf)->addr) +#define netbuf_fromport(buf) ((buf)->port) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/lab/net/lwip/include/lwip/netdb.h b/lab/net/lwip/include/lwip/netdb.h new file mode 100644 index 0000000..ce175ce --- /dev/null +++ b/lab/net/lwip/include/lwip/netdb.h @@ -0,0 +1,109 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/sockets.h" + +/* some rarely used options */ +#ifndef LWIP_DNS_API_DECLARE_H_ERRNO +#define LWIP_DNS_API_DECLARE_H_ERRNO 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_ERRORS +#define LWIP_DNS_API_DEFINE_ERRORS 1 +#endif + +#ifndef LWIP_DNS_API_DECLARE_STRUCTS +#define LWIP_DNS_API_DECLARE_STRUCTS 1 +#endif + +#if LWIP_DNS_API_DEFINE_ERRORS +/** Errors used by the DNS API functions, h_errno can be one of them */ +#define EAI_NONAME 200 +#define EAI_SERVICE 201 +#define EAI_FAIL 202 +#define EAI_MEMORY 203 + +#define HOST_NOT_FOUND 210 +#define NO_DATA 211 +#define NO_RECOVERY 212 +#define TRY_AGAIN 213 +#endif /* LWIP_DNS_API_DEFINE_ERRORS */ + +#if LWIP_DNS_API_DECLARE_STRUCTS +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Address family of socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol of socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address of socket. */ + char *ai_canonname; /* Canonical name of service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +#endif /* LWIP_DNS_API_DECLARE_STRUCTS */ + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessable error code set by the DNS API functions */ +extern int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/ + +struct hostent *lwip_gethostbyname(const char *name); +int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop); +void lwip_freeaddrinfo(struct addrinfo *ai); +int lwip_getaddrinfo(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); + +#if LWIP_COMPAT_SOCKETS +#define gethostbyname(name) lwip_gethostbyname(name) +#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \ + lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop) +#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(a) +#define getaddrinfo(nodname, servname, hints, res) \ + lwip_getaddrinfo(nodname, servname, hints, res) +#endif /* LWIP_COMPAT_SOCKETS */ + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/lab/net/lwip/include/lwip/netif.h b/lab/net/lwip/include/lwip/netif.h new file mode 100644 index 0000000..a325030 --- /dev/null +++ b/lab/net/lwip/include/lwip/netif.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETIF_H__ +#define __LWIP_NETIF_H__ + +#include "lwip/opt.h" + +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + +#include "lwip/err.h" + +#include "lwip/ip_addr.h" + +#include "lwip/inet.h" +#include "lwip/pbuf.h" +#if LWIP_DHCP +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + +/** must be the maximum of all used hardware address lengths + across all types of interfaces in use */ +#define NETIF_MAX_HWADDR_LEN 6U + +/** TODO: define the use (where, when, whom) of netif flags */ + +/** whether the network interface is 'up'. this is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + */ +#define NETIF_FLAG_UP 0x01U +/** if set, the netif has broadcast capability */ +#define NETIF_FLAG_BROADCAST 0x02U +/** if set, the netif is one end of a point-to-point connection */ +#define NETIF_FLAG_POINTTOPOINT 0x04U +/** if set, the interface is configured using DHCP */ +#define NETIF_FLAG_DHCP 0x08U +/** if set, the interface has an active link + * (set by the network interface driver) */ +#define NETIF_FLAG_LINK_UP 0x10U +/** if set, the netif is an device using ARP */ +#define NETIF_FLAG_ETHARP 0x20U +/** if set, the netif has IGMP capability */ +#define NETIF_FLAG_IGMP 0x40U + +/** Generic data structure used for all lwIP network interfaces. + * The following fields should be filled in by the initialization + * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ + +struct netif { + /** pointer to next in linked list */ + struct netif *next; + + /** IP address configuration in network byte order */ + struct ip_addr ip_addr; + struct ip_addr netmask; + struct ip_addr gw; + + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + err_t (* input)(struct pbuf *p, struct netif *inp); + /** This function is called by the IP module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + err_t (* output)(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr); + /** This function is called by the ARP module when it wants + * to send a packet on the interface. This function outputs + * the pbuf as-is on the link medium. */ + err_t (* linkoutput)(struct netif *netif, struct pbuf *p); +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + void (* status_callback)(struct netif *netif); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + void (* link_callback)(struct netif *netif); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + /** This field can be set by the device driver and could point + * to state information for the device. */ + void *state; +#if LWIP_DHCP + /** the DHCP client state information for this netif */ + struct dhcp *dhcp; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /** the AutoIP client state information for this netif */ + struct autoip *autoip; +#endif +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + /** number of bytes used in hwaddr */ + u8_t hwaddr_len; + /** link level hardware address of this interface */ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + /** maximum transfer unit (in bytes) */ + u16_t mtu; + /** flags (see NETIF_FLAG_ above) */ + u8_t flags; + /** descriptive abbreviation */ + char name[2]; + /** number of this interface */ + u8_t num; +#if LWIP_SNMP + /** link type (from "snmp_ifType" enum from snmp.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + u32_t ifinoctets; + u32_t ifinucastpkts; + u32_t ifinnucastpkts; + u32_t ifindiscards; + u32_t ifoutoctets; + u32_t ifoutucastpkts; + u32_t ifoutnucastpkts; + u32_t ifoutdiscards; +#endif /* LWIP_SNMP */ +#if LWIP_IGMP + /* This function could be called to add or delete a entry in the multicast filter table of the ethernet MAC.*/ + err_t (*igmp_mac_filter)( struct netif *netif, struct ip_addr *group, u8_t action); +#endif /* LWIP_IGMP */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + struct pbuf *loop_first; + struct pbuf *loop_last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ +}; + +#if LWIP_SNMP +#define NETIF_INIT_SNMP(netif, type, speed) \ + /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \ + netif->link_type = type; \ + /* your link speed here (units: bits per second) */ \ + netif->link_speed = speed; \ + netif->ts = 0; \ + netif->ifinoctets = 0; \ + netif->ifinucastpkts = 0; \ + netif->ifinnucastpkts = 0; \ + netif->ifindiscards = 0; \ + netif->ifoutoctets = 0; \ + netif->ifoutucastpkts = 0; \ + netif->ifoutnucastpkts = 0; \ + netif->ifoutdiscards = 0 +#else /* LWIP_SNMP */ +#define NETIF_INIT_SNMP(netif, type, speed) +#endif /* LWIP_SNMP */ + + +/** The list of network interfaces. */ +extern struct netif *netif_list; +/** The default network interface. */ +extern struct netif *netif_default; + +#define netif_init() /* Compatibility define, not init needed. */ + +struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)); + +void +netif_set_addr(struct netif *netif,struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw); +void netif_remove(struct netif * netif); + +/* Returns a network interface given its name. The name is of the form + "et0", where the first two letters are the "name" field in the + netif structure, and the digit is in the num field in the same + structure. */ +struct netif *netif_find(char *name); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr); +void netif_set_netmask(struct netif *netif, struct ip_addr *netmask); +void netif_set_gw(struct netif *netif, struct ip_addr *gw); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +u8_t netif_is_up(struct netif *netif); + +#if LWIP_NETIF_STATUS_CALLBACK +/* + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, void (* status_callback)(struct netif *netif)); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +u8_t netif_is_link_up(struct netif *netif); +/* + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif)); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#ifdef __cplusplus +} +#endif + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, struct ip_addr *dest_ip); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#endif /* __LWIP_NETIF_H__ */ diff --git a/lab/net/lwip/include/lwip/netifapi.h b/lab/net/lwip/include/lwip/netifapi.h new file mode 100644 index 0000000..36c6bd0 --- /dev/null +++ b/lab/net/lwip/include/lwip/netifapi.h @@ -0,0 +1,100 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef __LWIP_NETIFAPI_H__ +#define __LWIP_NETIFAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netifapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + struct netif *netif; + union { + struct { + struct ip_addr *ipaddr; + struct ip_addr *netmask; + struct ip_addr *gw; + void *state; + err_t (* init) (struct netif *netif); + err_t (* input)(struct pbuf *p, struct netif *netif); + } add; + struct { + void (* voidfunc)(struct netif *netif); + err_t (* errtfunc)(struct netif *netif); + } common; + } msg; +}; + +struct netifapi_msg { + void (* function)(struct netifapi_msg_msg *msg); + struct netifapi_msg_msg msg; +}; + + +/* API for application */ +err_t netifapi_netif_add ( struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif) ); + +err_t netifapi_netif_common ( struct netif *netif, + void (* voidfunc)(struct netif *netif), + err_t (* errtfunc)(struct netif *netif) ); + +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* __LWIP_NETIFAPI_H__ */ diff --git a/lab/net/lwip/include/lwip/opt.h b/lab/net/lwip/include/lwip/opt.h new file mode 100644 index 0000000..8fd9442 --- /dev/null +++ b/lab/net/lwip/include/lwip/opt.h @@ -0,0 +1,1704 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_OPT_H__ +#define __LWIP_OPT_H__ + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ +#include "lwipopts.h" +#include "lwip/debug.h" + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#ifndef SYS_LIGHTWEIGHT_PROT +#define SYS_LIGHTWEIGHT_PROT 0 +#endif + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#ifndef NO_SYS +#define NO_SYS 0 +#endif + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ +#ifndef MEM_ALIGNMENT +#define MEM_ALIGNMENT 1 +#endif + +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ +#ifndef MEM_SIZE +#define MEM_SIZE 1600 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#ifndef MEMP_SANITY_CHECK +#define MEMP_SANITY_CHECK 0 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the lenght needed is returned. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#ifndef MEMP_NUM_PBUF +#define MEMP_NUM_PBUF 16 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#ifndef MEMP_NUM_RAW_PCB +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#ifndef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB 5 +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB_LISTEN +#define MEMP_NUM_TCP_PCB_LISTEN 8 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_SEG +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of simultaneously IP packets queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT 3 +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETBUF +#define MEMP_NUM_NETBUF 2 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 8 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 16 +#endif + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 10 +#endif + +/** + * ARP_QUEUEING==1: Outgoing packets are queued during hardware address + * resolution. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 1 +#endif + +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 1 +#endif + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#ifndef IP_FORWARD +#define IP_FORWARD 0 +#endif + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 1 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented. + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 1 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef LWIP_RAW +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef RAW_TTL +#define RAW_TTL (IP_DEFAULT_TTL) +#endif + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 0 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 1 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * SNMP_PRIVATE_MIB: + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 1 +#endif + +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS use a local buffer if DNS_USES_STATIC_BUF=0, a static one if + DNS_USES_STATIC_BUF=1, or a dynamic one if DNS_USES_STATIC_BUF=2. + The buffer will be of size DNS_MSG_SIZE */ +#ifndef DNS_USES_STATIC_BUF +#define DNS_USES_STATIC_BUF 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#ifndef LWIP_UDP +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#ifndef UDP_TTL +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#ifndef LWIP_TCP +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#ifndef TCP_TTL +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. + */ +#ifndef TCP_WND +#define TCP_WND 2048 +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 12 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 6 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ 1 +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 128, a *very* + * conservative default.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 128 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + */ +#ifndef TCP_SND_BUF +#define TCP_SND_BUF 256 +#endif + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +#ifndef TCP_SND_QUEUELEN +#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF/TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than or equal + * to TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable. + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT (TCP_SND_BUF/2) +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. + */ +#ifndef LWIP_EVENT_API +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#else +#define LWIP_EVENT_API 1 +#define LWIP_CALLBACK_API 0 +#endif + + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN 14 +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 0 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_PRIO +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_PRIO +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * PPP_THREAD_NAME: The name assigned to the pppMain thread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppMain" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppMain thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppMain thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_PRIO +#define PPP_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_PRIO +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 1 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR and SO_REUSEPORT options. DO NOT USE! + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 1 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#ifndef LINK_STATS +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#ifndef IP_STATS +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#ifndef IPFRAG_STATS +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#ifndef ICMP_STATS +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS 1 +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS 1 +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS 1 +#endif + +#else + +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 + +#endif /* LWIP_STATS */ + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +#if PPP_SUPPORT + +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif + +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif + +/* + * Timeouts + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +/* Interval in seconds between keepalive echo requests, 0 to disable. */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ +#define PPP_MAXMTU 1500 /* Largest MTU we allow */ +#endif +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 296 /* Try for this */ +#endif +#define PPP_MINMRU 128 /* No MRUs below this */ + + +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#define MAXSECRETLEN 256 /* max length of password or secret */ + +#endif /* PPP_SUPPORT */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#ifndef CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#ifndef CHECKSUM_GEN_UDP +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#ifndef CHECKSUM_GEN_TCP +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#ifndef CHECKSUM_CHECK_IP +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#ifndef CHECKSUM_CHECK_UDP +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#ifndef CHECKSUM_CHECK_TCP +#define CHECKSUM_CHECK_TCP 1 +#endif + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_OFF +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#ifndef TCP_FR_DEBUG +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#ifndef TCP_RTO_DEBUG +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +#endif /* __LWIP_OPT_H__ */ diff --git a/lab/net/lwip/include/lwip/pbuf.h b/lab/net/lwip/include/lwip/pbuf.h new file mode 100644 index 0000000..57a1e99 --- /dev/null +++ b/lab/net/lwip/include/lwip/pbuf.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_PBUF_H__ +#define __LWIP_PBUF_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PBUF_TRANSPORT_HLEN 20 +#define PBUF_IP_HLEN 20 + +typedef enum { + PBUF_TRANSPORT, + PBUF_IP, + PBUF_LINK, + PBUF_RAW +} pbuf_layer; + +typedef enum { + PBUF_RAM, /* pbuf data is stored in RAM */ + PBUF_ROM, /* pbuf data is stored in ROM */ + PBUF_REF, /* pbuf comes from the pbuf pool */ + PBUF_POOL /* pbuf payload refers to RAM */ +} pbuf_type; + + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U + +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; + +}; + +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() + +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t size, pbuf_type type); +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +void pbuf_ref_chain(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/lab/net/lwip/include/lwip/raw.h b/lab/net/lwip/include/lwip/raw.h new file mode 100644 index 0000000..20b0a11 --- /dev/null +++ b/lab/net/lwip/include/lwip/raw.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_RAW_H__ +#define __LWIP_RAW_H__ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/inet.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb { +/* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /* receive callback function + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ + u8_t (* recv)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + struct ip_addr *addr); + /* user-supplied argument for the recv callback */ + void *recv_arg; +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, struct ip_addr *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, struct ip_addr *ipaddr); + +void raw_recv (struct raw_pcb *pcb, + u8_t (* recv)(void *arg, struct raw_pcb *pcb, + struct pbuf *p, + struct ip_addr *addr), + void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr); +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/lab/net/lwip/include/lwip/sio.h b/lab/net/lwip/include/lwip/sio.h new file mode 100644 index 0000000..7d9162e --- /dev/null +++ b/lab/net/lwip/include/lwip/sio.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * This is the interface to the platform specific serial IO module + * It needs to be implemented by those platforms which need SLIP or PPP + */ + +#ifndef __SIO_H__ +#define __SIO_H__ + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If you want to define sio_fd_t elsewhere or differently, + define this in your cc.h file. */ +#ifndef __sio_fd_t_defined +typedef void * sio_fd_t; +#endif + +/* The following functions can be defined to something else in your cc.h file + or be implemented in your custom sio.c file. */ + +#ifndef sio_open +sio_fd_t sio_open(u8_t); +#endif + +#ifndef sio_send +void sio_send(u8_t, sio_fd_t); +#endif + +#ifndef sio_recv +u8_t sio_recv(sio_fd_t); +#endif + +#ifndef sio_read +u32_t sio_read(sio_fd_t, u8_t *, u32_t); +#endif + +#ifndef sio_write +u32_t sio_write(sio_fd_t, u8_t *, u32_t); +#endif + +#ifndef sio_read_abort +void sio_read_abort(sio_fd_t); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/lab/net/lwip/include/lwip/snmp.h b/lab/net/lwip/include/lwip/snmp.h new file mode 100644 index 0000000..dd03d5d --- /dev/null +++ b/lab/net/lwip/include/lwip/snmp.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2001, 2002 Leon Woestenberg + * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef __LWIP_SNMP_H__ +#define __LWIP_SNMP_H__ + +#include "lwip/opt.h" +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** SNMP "sysuptime" Interval */ +#define SNMP_SYSUPTIME_INTERVAL 10 + +/** fixed maximum length for object identifier type */ +#define LWIP_SNMP_OBJ_ID_LEN 32 + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + s32_t id[LWIP_SNMP_OBJ_ID_LEN]; +}; + +/* system */ +void snmp_set_sysdesr(u8_t* str, u8_t* len); +void snmp_set_sysobjid(struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid); +void snmp_inc_sysuptime(void); +void snmp_add_sysuptime(u32_t value); +void snmp_get_sysuptime(u32_t *value); +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen); + +/* network interface */ +void snmp_add_ifinoctets(struct netif *ni, u32_t value); +void snmp_inc_ifinucastpkts(struct netif *ni); +void snmp_inc_ifinnucastpkts(struct netif *ni); +void snmp_inc_ifindiscards(struct netif *ni); +void snmp_add_ifoutoctets(struct netif *ni, u32_t value); +void snmp_inc_ifoutucastpkts(struct netif *ni); +void snmp_inc_ifoutnucastpkts(struct netif *ni); +void snmp_inc_ifoutdiscards(struct netif *ni); +void snmp_inc_iflist(void); +void snmp_dec_iflist(void); + +/* ARP (for atTable and ipNetToMediaTable) */ +void snmp_insert_arpidx_tree(struct netif *ni, struct ip_addr *ip); +void snmp_delete_arpidx_tree(struct netif *ni, struct ip_addr *ip); + +/* IP */ +void snmp_inc_ipinreceives(void); +void snmp_inc_ipinhdrerrors(void); +void snmp_inc_ipinaddrerrors(void); +void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipinunknownprotos(void); +void snmp_inc_ipindiscards(void); +void snmp_inc_ipindelivers(void); +void snmp_inc_ipoutrequests(void); +void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipoutnoroutes(void); +void snmp_inc_ipreasmreqds(void); +void snmp_inc_ipreasmoks(void); +void snmp_inc_ipreasmfails(void); +void snmp_inc_ipfragoks(void); +void snmp_inc_ipfragfails(void); +void snmp_inc_ipfragcreates(void); +void snmp_inc_iproutingdiscards(void); +void snmp_insert_ipaddridx_tree(struct netif *ni); +void snmp_delete_ipaddridx_tree(struct netif *ni); +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni); +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni); + +/* ICMP */ +void snmp_inc_icmpinmsgs(void); +void snmp_inc_icmpinerrors(void); +void snmp_inc_icmpindestunreachs(void); +void snmp_inc_icmpintimeexcds(void); +void snmp_inc_icmpinparmprobs(void); +void snmp_inc_icmpinsrcquenchs(void); +void snmp_inc_icmpinredirects(void); +void snmp_inc_icmpinechos(void); +void snmp_inc_icmpinechoreps(void); +void snmp_inc_icmpintimestamps(void); +void snmp_inc_icmpintimestampreps(void); +void snmp_inc_icmpinaddrmasks(void); +void snmp_inc_icmpinaddrmaskreps(void); +void snmp_inc_icmpoutmsgs(void); +void snmp_inc_icmpouterrors(void); +void snmp_inc_icmpoutdestunreachs(void); +void snmp_inc_icmpouttimeexcds(void); +void snmp_inc_icmpoutparmprobs(void); +void snmp_inc_icmpoutsrcquenchs(void); +void snmp_inc_icmpoutredirects(void); +void snmp_inc_icmpoutechos(void); +void snmp_inc_icmpoutechoreps(void); +void snmp_inc_icmpouttimestamps(void); +void snmp_inc_icmpouttimestampreps(void); +void snmp_inc_icmpoutaddrmasks(void); +void snmp_inc_icmpoutaddrmaskreps(void); + +/* TCP */ +void snmp_inc_tcpactiveopens(void); +void snmp_inc_tcppassiveopens(void); +void snmp_inc_tcpattemptfails(void); +void snmp_inc_tcpestabresets(void); +void snmp_inc_tcpinsegs(void); +void snmp_inc_tcpoutsegs(void); +void snmp_inc_tcpretranssegs(void); +void snmp_inc_tcpinerrs(void); +void snmp_inc_tcpoutrsts(void); + +/* UDP */ +void snmp_inc_udpindatagrams(void); +void snmp_inc_udpnoports(void); +void snmp_inc_udpinerrors(void); +void snmp_inc_udpoutdatagrams(void); +void snmp_insert_udpidx_tree(struct udp_pcb *pcb); +void snmp_delete_udpidx_tree(struct udp_pcb *pcb); + +/* SNMP */ +void snmp_inc_snmpinpkts(void); +void snmp_inc_snmpoutpkts(void); +void snmp_inc_snmpinbadversions(void); +void snmp_inc_snmpinbadcommunitynames(void); +void snmp_inc_snmpinbadcommunityuses(void); +void snmp_inc_snmpinasnparseerrs(void); +void snmp_inc_snmpintoobigs(void); +void snmp_inc_snmpinnosuchnames(void); +void snmp_inc_snmpinbadvalues(void); +void snmp_inc_snmpinreadonlys(void); +void snmp_inc_snmpingenerrs(void); +void snmp_add_snmpintotalreqvars(u8_t value); +void snmp_add_snmpintotalsetvars(u8_t value); +void snmp_inc_snmpingetrequests(void); +void snmp_inc_snmpingetnexts(void); +void snmp_inc_snmpinsetrequests(void); +void snmp_inc_snmpingetresponses(void); +void snmp_inc_snmpintraps(void); +void snmp_inc_snmpouttoobigs(void); +void snmp_inc_snmpoutnosuchnames(void); +void snmp_inc_snmpoutbadvalues(void); +void snmp_inc_snmpoutgenerrs(void); +void snmp_inc_snmpoutgetrequests(void); +void snmp_inc_snmpoutgetnexts(void); +void snmp_inc_snmpoutsetrequests(void); +void snmp_inc_snmpoutgetresponses(void); +void snmp_inc_snmpouttraps(void); +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid); +void snmp_set_snmpenableauthentraps(u8_t *value); +void snmp_get_snmpenableauthentraps(u8_t *value); + +/* LWIP_SNMP support not available */ +/* define everything to be empty */ +#else + +/* system */ +#define snmp_set_sysdesr(str, len) +#define snmp_set_sysobjid(oid); +#define snmp_get_sysobjid_ptr(oid) +#define snmp_inc_sysuptime() +#define snmp_add_sysuptime(value) +#define snmp_get_sysuptime(value) +#define snmp_set_syscontact(ocstr, ocstrlen); +#define snmp_set_sysname(ocstr, ocstrlen); +#define snmp_set_syslocation(ocstr, ocstrlen); + +/* network interface */ +#define snmp_add_ifinoctets(ni,value) +#define snmp_inc_ifinucastpkts(ni) +#define snmp_inc_ifinnucastpkts(ni) +#define snmp_inc_ifindiscards(ni) +#define snmp_add_ifoutoctets(ni,value) +#define snmp_inc_ifoutucastpkts(ni) +#define snmp_inc_ifoutnucastpkts(ni) +#define snmp_inc_ifoutdiscards(ni) +#define snmp_inc_iflist() +#define snmp_dec_iflist() + +/* ARP */ +#define snmp_insert_arpidx_tree(ni,ip) +#define snmp_delete_arpidx_tree(ni,ip) + +/* IP */ +#define snmp_inc_ipinreceives() +#define snmp_inc_ipinhdrerrors() +#define snmp_inc_ipinaddrerrors() +#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipinunknownprotos() +#define snmp_inc_ipindiscards() +#define snmp_inc_ipindelivers() +#define snmp_inc_ipoutrequests() +#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipoutnoroutes() +#define snmp_inc_ipreasmreqds() +#define snmp_inc_ipreasmoks() +#define snmp_inc_ipreasmfails() +#define snmp_inc_ipfragoks() +#define snmp_inc_ipfragfails() +#define snmp_inc_ipfragcreates() +#define snmp_inc_iproutingdiscards() +#define snmp_insert_ipaddridx_tree(ni) +#define snmp_delete_ipaddridx_tree(ni) +#define snmp_insert_iprteidx_tree(dflt, ni) +#define snmp_delete_iprteidx_tree(dflt, ni) + +/* ICMP */ +#define snmp_inc_icmpinmsgs() +#define snmp_inc_icmpinerrors() +#define snmp_inc_icmpindestunreachs() +#define snmp_inc_icmpintimeexcds() +#define snmp_inc_icmpinparmprobs() +#define snmp_inc_icmpinsrcquenchs() +#define snmp_inc_icmpinredirects() +#define snmp_inc_icmpinechos() +#define snmp_inc_icmpinechoreps() +#define snmp_inc_icmpintimestamps() +#define snmp_inc_icmpintimestampreps() +#define snmp_inc_icmpinaddrmasks() +#define snmp_inc_icmpinaddrmaskreps() +#define snmp_inc_icmpoutmsgs() +#define snmp_inc_icmpouterrors() +#define snmp_inc_icmpoutdestunreachs() +#define snmp_inc_icmpouttimeexcds() +#define snmp_inc_icmpoutparmprobs() +#define snmp_inc_icmpoutsrcquenchs() +#define snmp_inc_icmpoutredirects() +#define snmp_inc_icmpoutechos() +#define snmp_inc_icmpoutechoreps() +#define snmp_inc_icmpouttimestamps() +#define snmp_inc_icmpouttimestampreps() +#define snmp_inc_icmpoutaddrmasks() +#define snmp_inc_icmpoutaddrmaskreps() +/* TCP */ +#define snmp_inc_tcpactiveopens() +#define snmp_inc_tcppassiveopens() +#define snmp_inc_tcpattemptfails() +#define snmp_inc_tcpestabresets() +#define snmp_inc_tcpinsegs() +#define snmp_inc_tcpoutsegs() +#define snmp_inc_tcpretranssegs() +#define snmp_inc_tcpinerrs() +#define snmp_inc_tcpoutrsts() + +/* UDP */ +#define snmp_inc_udpindatagrams() +#define snmp_inc_udpnoports() +#define snmp_inc_udpinerrors() +#define snmp_inc_udpoutdatagrams() +#define snmp_insert_udpidx_tree(pcb) +#define snmp_delete_udpidx_tree(pcb) + +/* SNMP */ +#define snmp_inc_snmpinpkts() +#define snmp_inc_snmpoutpkts() +#define snmp_inc_snmpinbadversions() +#define snmp_inc_snmpinbadcommunitynames() +#define snmp_inc_snmpinbadcommunityuses() +#define snmp_inc_snmpinasnparseerrs() +#define snmp_inc_snmpintoobigs() +#define snmp_inc_snmpinnosuchnames() +#define snmp_inc_snmpinbadvalues() +#define snmp_inc_snmpinreadonlys() +#define snmp_inc_snmpingenerrs() +#define snmp_add_snmpintotalreqvars(value) +#define snmp_add_snmpintotalsetvars(value) +#define snmp_inc_snmpingetrequests() +#define snmp_inc_snmpingetnexts() +#define snmp_inc_snmpinsetrequests() +#define snmp_inc_snmpingetresponses() +#define snmp_inc_snmpintraps() +#define snmp_inc_snmpouttoobigs() +#define snmp_inc_snmpoutnosuchnames() +#define snmp_inc_snmpoutbadvalues() +#define snmp_inc_snmpoutgenerrs() +#define snmp_inc_snmpoutgetrequests() +#define snmp_inc_snmpoutgetnexts() +#define snmp_inc_snmpoutsetrequests() +#define snmp_inc_snmpoutgetresponses() +#define snmp_inc_snmpouttraps() +#define snmp_get_snmpgrpid_ptr(oid) +#define snmp_set_snmpenableauthentraps(value) +#define snmp_get_snmpenableauthentraps(value) + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_H__ */ diff --git a/lab/net/lwip/include/lwip/snmp_asn1.h b/lab/net/lwip/include/lwip/snmp_asn1.h new file mode 100644 index 0000000..3d70d33 --- /dev/null +++ b/lab/net/lwip/include/lwip/snmp_asn1.h @@ -0,0 +1,97 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_ASN1_H__ +#define __LWIP_SNMP_ASN1_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/snmp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_UNIV (!0x80 | !0x40) +#define SNMP_ASN1_APPLIC (!0x80 | 0x40) +#define SNMP_ASN1_CONTXT ( 0x80 | !0x40) + +#define SNMP_ASN1_CONSTR (0x20) +#define SNMP_ASN1_PRIMIT (!0x20) + +/* universal tags */ +#define SNMP_ASN1_INTEG 2 +#define SNMP_ASN1_OC_STR 4 +#define SNMP_ASN1_NUL 5 +#define SNMP_ASN1_OBJ_ID 6 +#define SNMP_ASN1_SEQ 16 + +/* application specific (SNMP) tags */ +#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */ +#define SNMP_ASN1_COUNTER 1 /* u32_t */ +#define SNMP_ASN1_GAUGE 2 /* u32_t */ +#define SNMP_ASN1_TIMETICKS 3 /* u32_t */ +#define SNMP_ASN1_OPAQUE 4 /* octet string */ + +/* context specific (SNMP) tags */ +#define SNMP_ASN1_PDU_GET_REQ 0 +#define SNMP_ASN1_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_PDU_GET_RESP 2 +#define SNMP_ASN1_PDU_SET_REQ 3 +#define SNMP_ASN1_PDU_TRAP 4 + +err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type); +err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length); +err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid); +err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed); +err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type); +err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length); +err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, u32_t value); +err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_t value); +err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident); +err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_ASN1_H__ */ diff --git a/lab/net/lwip/include/lwip/snmp_msg.h b/lab/net/lwip/include/lwip/snmp_msg.h new file mode 100644 index 0000000..d202825 --- /dev/null +++ b/lab/net/lwip/include/lwip/snmp_msg.h @@ -0,0 +1,307 @@ +/** + * @file + * SNMP Agent message handling structures. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_MSG_H__ +#define __LWIP_SNMP_MSG_H__ + +#include "lwip/opt.h" +#include "lwip/snmp.h" +#include "lwip/snmp_structs.h" + +#if SNMP_PRIVATE_MIB +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +#define SNMP_ES_NOERROR 0 +#define SNMP_ES_TOOBIG 1 +#define SNMP_ES_NOSUCHNAME 2 +#define SNMP_ES_BADVALUE 3 +#define SNMP_ES_READONLY 4 +#define SNMP_ES_GENERROR 5 + +#define SNMP_GENTRAP_COLDSTART 0 +#define SNMP_GENTRAP_WARMSTART 1 +#define SNMP_GENTRAP_AUTHFAIL 4 +#define SNMP_GENTRAP_ENTERPRISESPC 6 + +struct snmp_varbind +{ + /* next pointer, NULL for last in list */ + struct snmp_varbind *next; + /* previous pointer, NULL for first in list */ + struct snmp_varbind *prev; + + /* object identifier length (in s32_t) */ + u8_t ident_len; + /* object identifier array */ + s32_t *ident; + + /* object value ASN1 type */ + u8_t value_type; + /* object value length (in u8_t) */ + u8_t value_len; + /* object value */ + void *value; + + /* encoding varbind seq length length */ + u8_t seqlenlen; + /* encoding object identifier length length */ + u8_t olenlen; + /* encoding object value length length */ + u8_t vlenlen; + /* encoding varbind seq length */ + u16_t seqlen; + /* encoding object identifier length */ + u16_t olen; + /* encoding object value length */ + u16_t vlen; +}; + +struct snmp_varbind_root +{ + struct snmp_varbind *head; + struct snmp_varbind *tail; + /* number of variable bindings in list */ + u8_t count; + /* encoding varbind-list seq length length */ + u8_t seqlenlen; + /* encoding varbind-list seq length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_resp_header_lengths +{ + /* encoding error-index length length */ + u8_t erridxlenlen; + /* encoding error-status length length */ + u8_t errstatlenlen; + /* encoding request id length length */ + u8_t ridlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding error-index length */ + u16_t erridxlen; + /* encoding error-status length */ + u16_t errstatlen; + /* encoding request id length */ + u16_t ridlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_trap_header_lengths +{ + /* encoding timestamp length length */ + u8_t tslenlen; + /* encoding specific-trap length length */ + u8_t strplenlen; + /* encoding generic-trap length length */ + u8_t gtrplenlen; + /* encoding agent-addr length length */ + u8_t aaddrlenlen; + /* encoding enterprise-id length length */ + u8_t eidlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding timestamp length */ + u16_t tslen; + /* encoding specific-trap length */ + u16_t strplen; + /* encoding generic-trap length */ + u16_t gtrplen; + /* encoding agent-addr length */ + u16_t aaddrlen; + /* encoding enterprise-id length */ + u16_t eidlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/* Accepting new SNMP messages. */ +#define SNMP_MSG_EMPTY 0 +/* Search for matching object for variable binding. */ +#define SNMP_MSG_SEARCH_OBJ 1 +/* Perform SNMP operation on in-memory object. + Pass-through states, for symmetry only. */ +#define SNMP_MSG_INTERNAL_GET_OBJDEF 2 +#define SNMP_MSG_INTERNAL_GET_VALUE 3 +#define SNMP_MSG_INTERNAL_SET_TEST 4 +#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5 +#define SNMP_MSG_INTERNAL_SET_VALUE 6 +/* Perform SNMP operation on object located externally. + In theory this could be used for building a proxy agent. + Practical use is for an enterprise spc. app. gateway. */ +#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7 +#define SNMP_MSG_EXTERNAL_GET_VALUE 8 +#define SNMP_MSG_EXTERNAL_SET_TEST 9 +#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10 +#define SNMP_MSG_EXTERNAL_SET_VALUE 11 + +#define SNMP_COMMUNITY_STR_LEN 64 +struct snmp_msg_pstat +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* source IP address */ + struct ip_addr sip; + /* source UDP port */ + u16_t sp; + /* request type */ + u8_t rt; + /* request ID */ + s32_t rid; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* community name (zero terminated) */ + u8_t community[SNMP_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u8_t com_strlen; + /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */ + u8_t state; + /* saved arguments for MSG_EXTERNAL_x */ + struct mib_external_node *ext_mib_node; + struct snmp_name_ptr ext_name_ptr; + struct obj_def ext_object_def; + struct snmp_obj_id ext_oid; + /* index into input variable binding list */ + u8_t vb_idx; + /* ptr into input variable binding list */ + struct snmp_varbind *vb_ptr; + /* list of variable bindings from input */ + struct snmp_varbind_root invb; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output response lengths used in ASN encoding */ + struct snmp_resp_header_lengths rhl; +}; + +struct snmp_msg_trap +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* destination IP address in network order */ + struct ip_addr dip; + + /* source enterprise ID (sysObjectID) */ + struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + u8_t sip_raw[4]; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output trap lengths used in ASN encoding */ + struct snmp_trap_header_lengths thl; +}; + +/** Agent Version constant, 0 = v1 oddity */ +extern const s32_t snmp_version; +/** Agent default "public" community string */ +extern const char snmp_publiccommunity[7]; + +extern struct snmp_msg_trap trap_msg; + +/** Agent setup, start listening to port 161. */ +void snmp_init(void); +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, struct ip_addr *dst); + +/** Varbind-list functions. */ +struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len); +void snmp_varbind_free(struct snmp_varbind *vb); +void snmp_varbind_list_free(struct snmp_varbind_root *root); +void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb); +struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root); + +/** Handle an internal (recv) or external (private response) event. */ +void snmp_msg_event(u8_t request_id); +err_t snmp_send_response(struct snmp_msg_pstat *m_stat); +err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap); +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_MSG_H__ */ diff --git a/lab/net/lwip/include/lwip/snmp_structs.h b/lab/net/lwip/include/lwip/snmp_structs.h new file mode 100644 index 0000000..9f3f8a9 --- /dev/null +++ b/lab/net/lwip/include/lwip/snmp_structs.h @@ -0,0 +1,262 @@ +/** + * @file + * Generic MIB tree structures. + * + * @todo namespace prefixes + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_STRUCTS_H__ +#define __LWIP_SNMP_STRUCTS_H__ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" + +#if SNMP_PRIVATE_MIB +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MIB object instance */ +#define MIB_OBJECT_NONE 0 +#define MIB_OBJECT_SCALAR 1 +#define MIB_OBJECT_TAB 2 + +/* MIB object access */ +#define MIB_OBJECT_READ_ONLY 0 +#define MIB_OBJECT_READ_WRITE 1 +#define MIB_OBJECT_WRITE_ONLY 2 +#define MIB_OBJECT_NOT_ACCESSIBLE 3 + +/** object definition returned by (get_object_def)() */ +struct obj_def +{ + /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */ + u8_t instance; + /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */ + u8_t access; + /* ASN type for this object */ + u8_t asn_type; + /* value length (host length) */ + u16_t v_len; + /* length of instance part of supplied object identifier */ + u8_t id_inst_len; + /* instance part of supplied object identifier */ + s32_t *id_inst_ptr; +}; + +struct snmp_name_ptr +{ + u8_t ident_len; + s32_t *ident; +}; + +/** MIB const scalar (.0) node */ +#define MIB_NODE_SC 0x01 +/** MIB const array node */ +#define MIB_NODE_AR 0x02 +/** MIB array node (mem_malloced from RAM) */ +#define MIB_NODE_RA 0x03 +/** MIB list root node (mem_malloced from RAM) */ +#define MIB_NODE_LR 0x04 +/** MIB node for external objects */ +#define MIB_NODE_EX 0x05 + +/** node "base class" layout, the mandatory fields for a node */ +struct mib_node +{ + /** returns struct obj_def for the given object identifier */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + /** returns object value for the given object identifier, + @note the caller must allocate at least len bytes for the value */ + void (*get_value)(struct obj_def *od, u16_t len, void *value); + /** tests length and/or range BEFORE setting */ + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + /** sets object value, only to be called when set_test() */ + void (*set_value)(struct obj_def *od, u16_t len, void *value); + /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */ + const u8_t node_type; + /* array or max list length */ + const u16_t maxlength; +}; + +/** derived node for scalars .0 index */ +typedef struct mib_node mib_scalar_node; + +/** derived node, points to a fixed size const array + of sub-identifiers plus a 'child' pointer */ +struct mib_array_node +{ + /* inherited "base class" members */ + void (* const get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (* const get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + const u8_t node_type; + const u16_t maxlength; + + /* aditional struct members */ + const s32_t *objid; + struct mib_node* const *nptr; +}; + +/** derived node, points to a fixed size mem_malloced array + of sub-identifiers plus a 'child' pointer */ +struct mib_ram_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + s32_t *objid; + struct mib_node **nptr; +}; + +struct mib_list_node +{ + struct mib_list_node *prev; + struct mib_list_node *next; + s32_t objid; + struct mib_node *nptr; +}; + +/** derived node, points to a doubly linked list + of sub-identifiers plus a 'child' pointer */ +struct mib_list_rootnode +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + struct mib_list_node *head; + struct mib_list_node *tail; + /* counts list nodes in list */ + u16_t count; +}; + +/** derived node, has access functions for mib object in external memory or device + using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */ +struct mib_external_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + /** points to an extenal (in memory) record of some sort of addressing + information, passed to and interpreted by the funtions below */ + void* addr_inf; + /** tree levels under this node */ + u8_t tree_levels; + /** number of objects at this level */ + u16_t (*level_length)(void* addr_inf, u8_t level); + /** compares object sub identifier with external id + return zero when equal, nonzero when unequal */ + s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id); + void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id); + + /** async Questions */ + void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_q)(u8_t rid, struct obj_def *od); + void (*set_test_q)(u8_t rid, struct obj_def *od); + void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Answers */ + void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Panic Close (agent returns error reply, + e.g. used for external transaction cleanup) */ + void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_pc)(u8_t rid, struct obj_def *od); + void (*set_test_pc)(u8_t rid, struct obj_def *od); + void (*set_value_pc)(u8_t rid, struct obj_def *od); +}; + +/** export MIB tree from mib2.c */ +extern const struct mib_array_node internet; + +/** dummy function pointers for non-leaf MIB nodes from mib2.c */ +void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +void noleafs_get_value(struct obj_def *od, u16_t len, void *value); +u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value); +void noleafs_set_value(struct obj_def *od, u16_t len, void *value); + +void snmp_oidtoip(s32_t *ident, struct ip_addr *ip); +void snmp_iptooid(struct ip_addr *ip, s32_t *ident); +void snmp_ifindextonetif(s32_t ifindex, struct netif **netif); +void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx); + +struct mib_list_node* snmp_mib_ln_alloc(s32_t id); +void snmp_mib_ln_free(struct mib_list_node *ln); +struct mib_list_rootnode* snmp_mib_lrn_alloc(void); +void snmp_mib_lrn_free(struct mib_list_rootnode *lrn); + +s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn); +s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn); +struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n); + +struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np); +struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); +u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident); +u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_STRUCTS_H__ */ diff --git a/lab/net/lwip/include/lwip/sockets.h b/lab/net/lwip/include/lwip/sockets.h new file mode 100644 index 0000000..e261798 --- /dev/null +++ b/lab/net/lwip/include/lwip/sockets.h @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +#ifndef __LWIP_SOCKETS_H__ +#define __LWIP_SOCKETS_H__ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/inet.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; + char sa_data[14]; +}; + +#ifndef socklen_t +# define socklen_t u32_t +#endif + +/* Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. These must match the SOF_ flags in ip.h! + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Unimplemented: allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_BROADCAST 0x0020 /* Unimplemented: permit sending of broadcast msgs */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ + +#define SO_DONTLINGER ((int)(~SO_LINGER)) + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#define PF_INET AF_INET +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#define IPPROTO_UDPLITE 136 + +#define INADDR_ANY 0 +#define INADDR_BROADCAST 0xffffffff + +/* Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ + + +#if LWIP_IGMP +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/* Socket flags: */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 04000U +#endif + +/* FD_SET used for lwip_select */ +#ifndef FD_SET + #undef FD_SETSIZE + /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ + #define FD_SETSIZE MEMP_NUM_NETCONN + #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) + #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) + #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) + #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p))) + + typedef struct fd_set { + unsigned char fd_bits [(FD_SETSIZE+7)/8]; + } fd_set; + +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ + +void lwip_socket_init(void); + +int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int lwip_bind(int s, struct sockaddr *name, socklen_t namelen); +int lwip_shutdown(int s, int how); +int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int lwip_close(int s); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_listen(int s, int backlog); +int lwip_recv(int s, void *mem, int len, unsigned int flags); +int lwip_read(int s, void *mem, int len); +int lwip_recvfrom(int s, void *mem, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen); +int lwip_send(int s, const void *dataptr, int size, unsigned int flags); +int lwip_sendto(int s, const void *dataptr, int size, unsigned int flags, + struct sockaddr *to, socklen_t tolen); +int lwip_socket(int domain, int type, int protocol); +int lwip_write(int s, const void *dataptr, int size); +int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +int lwip_ioctl(int s, long cmd, void *argp); + +#if LWIP_COMPAT_SOCKETS +#define accept(a,b,c) lwip_accept(a,b,c) +#define bind(a,b,c) lwip_bind(a,b,c) +#define shutdown(a,b) lwip_shutdown(a,b) +#define closesocket(s) lwip_close(s) +#define connect(a,b,c) lwip_connect(a,b,c) +#define getsockname(a,b,c) lwip_getsockname(a,b,c) +#define getpeername(a,b,c) lwip_getpeername(a,b,c) +#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e) +#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e) +#define listen(a,b) lwip_listen(a,b) +#define recv(a,b,c,d) lwip_recv(a,b,c,d) +#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f) +#define send(a,b,c,d) lwip_send(a,b,c,d) +#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f) +#define socket(a,b,c) lwip_socket(a,b,c) +#define select(a,b,c,d,e) lwip_select(a,b,c,d,e) +#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c) + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define read(a,b,c) lwip_read(a,b,c) +#define write(a,b,c) lwip_write(a,b,c) +#define close(s) lwip_close(s) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* __LWIP_SOCKETS_H__ */ diff --git a/lab/net/lwip/include/lwip/stats.h b/lab/net/lwip/include/lwip/stats.h new file mode 100644 index 0000000..aa179f5 --- /dev/null +++ b/lab/net/lwip/include/lwip/stats.h @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_STATS_H__ +#define __LWIP_STATS_H__ + +#include "lwip/opt.h" + +#include "lwip/mem.h" +#include "lwip/memp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_STATS + +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + +struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +struct stats_igmp { + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER v1_rxed; /* */ + STAT_COUNTER join_sent; /* */ + STAT_COUNTER leave_sent; /* */ + STAT_COUNTER unicast_query; /* */ + STAT_COUNTER report_sent; /* */ + STAT_COUNTER report_rxed; /* */ + STAT_COUNTER group_query_rxed; /* */ +}; + +struct stats_mem { + mem_size_t avail; + mem_size_t used; + mem_size_t max; + STAT_COUNTER err; + STAT_COUNTER illegal; +}; + +struct stats_syselem { + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; +}; + +struct stats_sys { + struct stats_syselem sem; + struct stats_syselem mbox; +}; + +struct stats_ { +#if LINK_STATS + struct stats_proto link; +#endif +#if ETHARP_STATS + struct stats_proto etharp; +#endif +#if IPFRAG_STATS + struct stats_proto ip_frag; +#endif +#if IP_STATS + struct stats_proto ip; +#endif +#if ICMP_STATS + struct stats_proto icmp; +#endif +#if IGMP_STATS + struct stats_igmp igmp; +#endif +#if UDP_STATS + struct stats_proto udp; +#endif +#if TCP_STATS + struct stats_proto tcp; +#endif +#if MEM_STATS + struct stats_mem mem; +#endif +#if MEMP_STATS + struct stats_mem memp[MEMP_MAX]; +#endif +#if SYS_STATS + struct stats_sys sys; +#endif +}; + +extern struct stats_ lwip_stats; + +#define stats_init() /* Compatibility define, not init needed. */ + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#else +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(x) +#endif /* LWIP_STATS */ + +#if TCP_STATS +#define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") +#else +#define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() +#endif + +#if UDP_STATS +#define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") +#else +#define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() +#endif + +#if ICMP_STATS +#define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") +#else +#define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp) +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() +#endif + +#if IP_STATS +#define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") +#else +#define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() +#endif + +#if IPFRAG_STATS +#define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") +#else +#define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() +#endif + +#if LINK_STATS +#define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") +#else +#define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) STATS_INC(mem.x) +#define MEM_STATS_INC_USED(x, y) do { lwip_stats.mem.used += y; \ + if (lwip_stats.mem.max < lwip_stats.mem.used) { \ + lwip_stats.mem.max = lwip_stats.mem.used; \ + } \ + } while(0) +#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + +#if MEMP_STATS +#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y +#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) +#define MEMP_STATS_INC_USED(x, i) do { ++lwip_stats.memp[i].used; \ + if (lwip_stats.memp[i].max < lwip_stats.memp[i].used) { \ + lwip_stats.memp[i].max = lwip_stats.memp[i].used; \ + } \ + } while(0) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) +#else +#define MEMP_STATS_AVAIL(x, i, y) +#define MEMP_STATS_INC(x, i) +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_INC_USED(x, i) +#define MEMP_STATS_DISPLAY(i) +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, char *name); +void stats_display_igmp(struct stats_igmp *igmp); +void stats_display_mem(struct stats_mem *mem, char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_STATS_H__ */ diff --git a/lab/net/lwip/include/lwip/sys.h b/lab/net/lwip/include/lwip/sys.h new file mode 100644 index 0000000..ce73bea --- /dev/null +++ b/lab/net/lwip/include/lwip/sys.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_SYS_H__ +#define __LWIP_SYS_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if NO_SYS + +/* For a totally minimal and standalone system, we provide null + definitions of the sys_ functions. */ +typedef u8_t sys_sem_t; +typedef u8_t sys_mbox_t; +typedef u8_t sys_prot_t; +struct sys_timeo {u8_t dummy;}; + +#define sys_init() +#define sys_timeout(m,h,a) +#define sys_untimeout(m,a) +#define sys_sem_new(c) c +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_sem_wait_timeout(s,t) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_mbox_new(s) 0 +#define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) +#define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) +#define sys_mbox_free(m) + +#define sys_thread_new(n,t,a,s,p) + +#else /* NO_SYS */ + +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/* sys_mbox_tryfetch returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" + +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +}; + +struct sys_timeouts { + struct sys_timeo *next; +}; + +/* sys_init() must be called before anthing else. */ +void sys_init(void); + +/* + * sys_timeout(): + * + * Schedule a timeout a specified amount of milliseconds in the + * future. When the timeout occurs, the specified timeout handler will + * be called. The handler will be passed the "arg" argument when + * called. + * + */ +void sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +void sys_untimeout(sys_timeout_handler h, void *arg); +struct sys_timeouts *sys_arch_timeouts(void); + +/* Semaphore functions. */ +sys_sem_t sys_sem_new(u8_t count); +void sys_sem_signal(sys_sem_t sem); +u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout); +void sys_sem_free(sys_sem_t sem); +void sys_sem_wait(sys_sem_t sem); +int sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout); + +/* Time functions. */ +#ifndef sys_msleep +void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */ +#endif +#ifndef sys_jiffies +u32_t sys_jiffies(void); /* since power up. */ +#endif + +/* Mailbox functions. */ +sys_mbox_t sys_mbox_new(int size); +void sys_mbox_post(sys_mbox_t mbox, void *msg); +err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg); +u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout); +#ifndef sys_arch_mbox_tryfetch /* Allow port to override with a macro */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg); +#endif +/* For now, we map straight to sys_arch implementation. */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) +void sys_mbox_free(sys_mbox_t mbox); +void sys_mbox_fetch(sys_mbox_t mbox, void **msg); + +/* Thread functions. */ +sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio); + +/* The following functions are used only in Unix code, and + can be omitted when porting the stack. */ +/* Returns the current time in microseconds. */ +unsigned long sys_now(void); + +#endif /* NO_SYS */ + +/* Critical Region Protection */ +/* These functions must be implemented in the sys_arch.c file. + In some implementations they can provide a more light-weight protection + mechanism than using semaphores. Otherwise semaphores can be used for + implementation */ +#ifndef SYS_ARCH_PROTECT +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#if SYS_LIGHTWEIGHT_PROT + +/** SYS_ARCH_DECL_PROTECT + * declare a protection variable. This macro will default to defining a variable of + * type sys_prot_t. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h. + */ +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev +/** SYS_ARCH_PROTECT + * Perform a "fast" protect. This could be implemented by + * disabling interrupts for an embedded system or by using a semaphore or + * mutex. The implementation should allow calling SYS_ARCH_PROTECT when + * already protected. The old protection level is returned in the variable + * "lev". This macro will default to calling the sys_arch_protect() function + * which should be implemented in sys_arch.c. If a particular port needs a + * different implementation, then this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() +/** SYS_ARCH_UNPROTECT + * Perform a "fast" set of the protection level to "lev". This could be + * implemented by setting the interrupt level to "lev" within the MACRO or by + * using a semaphore or mutex. This macro will default to calling the + * sys_arch_unprotect() function which should be implemented in + * sys_arch.c. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#endif /* SYS_ARCH_PROTECT */ + +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SYS_H__ */ diff --git a/lab/net/lwip/include/lwip/tcp.h b/lab/net/lwip/include/lwip/tcp.h new file mode 100644 index 0000000..883b0b8 --- /dev/null +++ b/lab/net/lwip/include/lwip/tcp.h @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_H__ +#define __LWIP_TCP_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +#define tcp_init() /* Compatibility define, not init needed. */ +void tcp_tmr (void); /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void); +struct tcp_pcb * tcp_alloc (u8_t prio); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)); +void tcp_recv (struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)); +void tcp_sent (struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)); +void tcp_poll (struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval); +void tcp_err (struct tcp_pcb *pcb, + void (* err)(void *arg, err_t err)); + +#define tcp_mss(pcb) ((pcb)->mss) +#define tcp_sndbuf(pcb) ((pcb)->snd_buf) + +#if TCP_LISTEN_BACKLOG +#define tcp_accepted(pcb) (((struct tcp_pcb_listen *)(pcb))->accepts_pending--) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) +#endif /* TCP_LISTEN_BACKLOG */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len); +err_t tcp_bind (struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port); +err_t tcp_connect (struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); + +/* Flags for "apiflags" parameter in tcp_write and tcp_enqueue */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + +err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +err_t tcp_output (struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); + +/** + * This is the Nagle algorithm: inhibit the sending of new TCP + * segments when new outgoing data arrives from the user if any + * previously transmitted data on the connection remains + * unacknowledged. + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & TF_NODELAY) || \ + (((tpcb)->unsent != NULL) && ((tpcb)->unsent->next != NULL))) ? \ + 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) + + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION() htonl(((u32_t)2 << 24) | \ + ((u32_t)4 << 16) | \ + (((u32_t)TCP_MSS / 256) << 8) | \ + (TCP_MSS & 255)) + +#define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((a)-(b)) >= 0) +/* is b<=a<=c? */ +#if 0 /* see bug #10548 */ +#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) +#endif +#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) +#define TCP_FIN 0x01U +#define TCP_SYN 0x02U +#define TCP_RST 0x04U +#define TCP_PSH 0x08U +#define TCP_ACK 0x10U +#define TCP_URG 0x20U +#define TCP_ECE 0x40U +#define TCP_CWR 0x80U + +#define TCP_FLAGS 0x3fU + +/* Length of the TCP header, excluding options. */ +#define TCP_HLEN 20 + +#ifndef TCP_TMR_INTERVAL +#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ +#endif /* TCP_TMR_INTERVAL */ + +#ifndef TCP_FAST_INTERVAL +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ +#endif /* TCP_FAST_INTERVAL */ + +#ifndef TCP_SLOW_INTERVAL +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ +#endif /* TCP_SLOW_INTERVAL */ + +#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 60000U /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); + PACK_STRUCT_FIELD(u32_t seqno); + PACK_STRUCT_FIELD(u32_t ackno); + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); + PACK_STRUCT_FIELD(u16_t wnd); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t urgp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8) +#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr)) +#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons((ntohs((phdr)->_hdrlen_rsvd_flags) & ~TCP_FLAGS) | (flags)) +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (flags)) +#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) ) + +#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & TCP_FIN || \ + TCPH_FLAGS((seg)->tcphdr) & TCP_SYN)? 1: 0)) + +enum tcp_state { + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 +}; + +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + void *callback_arg; \ + /* ports are in host byte order */ \ + u16_t local_port + +/* the TCP protocol control block */ +struct tcp_pcb { +/** common PCB members */ + IP_PCB; +/** protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb); + + /* ports are in host byte order */ + u16_t remote_port; + + u8_t flags; +#define TF_ACK_DELAY (u8_t)0x01U /* Delayed ACK. */ +#define TF_ACK_NOW (u8_t)0x02U /* Immediate ACK. */ +#define TF_INFR (u8_t)0x04U /* In fast recovery. */ +#define TF_FIN (u8_t)0x20U /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY (u8_t)0x40U /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR (u8_t)0x80U /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + u16_t rcv_wnd; /* receiver window */ + u16_t rcv_ann_wnd; /* announced receive window */ + + /* Timers */ + u32_t tmr; + u8_t polltmr, pollinterval; + + /* Retransmission timer. */ + s16_t rtime; + + u16_t mss; /* maximum segment size */ + + /* RTT (round trip time) estimation variables */ + u32_t rttest; /* RTT estimate in 500ms ticks */ + u32_t rtseq; /* sequence number being timed */ + s16_t sa, sv; /* @todo document this */ + + s16_t rto; /* retransmission time-out */ + u8_t nrtx; /* number of retransmissions */ + + /* fast retransmit/recovery */ + u32_t lastack; /* Highest acknowledged seqno. */ + u8_t dupacks; + + /* congestion avoidance/control variables */ + u16_t cwnd; + u16_t ssthresh; + + /* sender variables */ + u32_t snd_nxt, /* next seqno to be sent */ + snd_max; /* Highest seqno sent. */ + u16_t snd_wnd; /* sender window */ + u32_t snd_wl1, snd_wl2, /* Sequence and acknowledgement numbers of last + window update. */ + snd_lbb; /* Sequence number of next byte to be buffered. */ + + u16_t acked; + + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffff-3) + u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ + + + /* These are ordered by sequence number: */ + struct tcp_seg *unsent; /* Unsent (queued) segments. */ + struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ +#if TCP_QUEUE_OOSEQ + struct tcp_seg *ooseq; /* Received out of sequence segments. */ +#endif /* TCP_QUEUE_OOSEQ */ + + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + +#if LWIP_CALLBACK_API + /* Function to be called when more send buffer space is available. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb which has send buffer space available + * @param space the amount of bytes available + * @return ERR_OK: try to send some data by calling tcp_output + */ + err_t (* sent)(void *arg, struct tcp_pcb *pcb, u16_t space); + + /* Function to be called when (in-sequence) data has arrived. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb for which data has arrived + * @param p the packet buffer which arrived + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: try to send some data by calling tcp_output + */ + err_t (* recv)(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); + + /* Function to be called when a connection has been set up. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return value is currently ignored + */ + err_t (* connected)(void *arg, struct tcp_pcb *pcb, err_t err); + + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err); + + /* Function which is called periodically. + * The period can be adjusted in multiples of the TCP slow timer interval + * by changing tcp_pcb.polltmr. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb to poll for + * @return ERR_OK: try to send some data by calling tcp_output + */ + err_t (* poll)(void *arg, struct tcp_pcb *pcb); + + /* Function to be called whenever a fatal error occurs. + * There is no pcb parameter since most of the times, the pcb is + * already deallocated (or there is no pcb) when this function is called. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param err an indication why the error callback is called: + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ + void (* errf)(void *arg, err_t err); +#endif /* LWIP_CALLBACK_API */ + + /* idle time before KEEPALIVE is sent */ + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + + /* Persist timer counter */ + u32_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; +}; + +struct tcp_pcb_listen { +/* Common members of all PCB types */ + IP_PCB; +/* Protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb_listen); + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err); +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ +}; + +#if LWIP_EVENT_API + +enum lwip_event { + LWIP_EVENT_ACCEPT, + LWIP_EVENT_SENT, + LWIP_EVENT_RECV, + LWIP_EVENT_CONNECTED, + LWIP_EVENT_POLL, + LWIP_EVENT_ERR +}; + +err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb, + enum lwip_event, + struct pbuf *p, + u16_t size, + err_t err); + +#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_ACCEPT, NULL, 0, err) +#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_SENT, NULL, space, ERR_OK) +#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, (p), 0, (err)) +#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_CONNECTED, NULL, 0, (err)) +#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_POLL, NULL, 0, ERR_OK) +#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \ + LWIP_EVENT_ERR, NULL, 0, (err)) +#else /* LWIP_EVENT_API */ +#define TCP_EVENT_ACCEPT(pcb,err,ret) \ + if((pcb)->accept != NULL) \ + (ret = (pcb)->accept((pcb)->callback_arg,(pcb),(err))) +#define TCP_EVENT_SENT(pcb,space,ret) \ + if((pcb)->sent != NULL) \ + (ret = (pcb)->sent((pcb)->callback_arg,(pcb),(space))) +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + if((pcb)->recv != NULL) \ + { ret = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); } else { \ + ret = ERR_OK; \ + if (p) pbuf_free(p); } +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + if((pcb)->connected != NULL) \ + (ret = (pcb)->connected((pcb)->callback_arg,(pcb),(err))) +#define TCP_EVENT_POLL(pcb,ret) \ + if((pcb)->poll != NULL) \ + (ret = (pcb)->poll((pcb)->callback_arg,(pcb))) +#define TCP_EVENT_ERR(errf,arg,err) \ + if((errf) != NULL) \ + (errf)((arg),(err)) +#endif /* LWIP_EVENT_API */ + +/* This structure represents a TCP segment on the unsent and unacked queues */ +struct tcp_seg { + struct tcp_seg *next; /* used when putting segements on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + void *dataptr; /* pointer to the TCP data in the pbuf */ + u16_t len; /* the TCP length of this segment */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +/* Internal functions and global variables: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +u8_t tcp_segs_free(struct tcp_seg *seg); +u8_t tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#define tcp_ack(pcb) if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + tcp_output(pcb); \ + } else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } + +#define tcp_ack_now(pcb) (pcb)->flags |= TF_ACK_NOW; \ + tcp_output(pcb) + +err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags); +err_t tcp_enqueue(struct tcp_pcb *pcb, void *dataptr, u16_t len, + u8_t flags, u8_t apiflags, + u8_t *optdata, u8_t optlen); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst(u32_t seqno, u32_t ackno, + struct ip_addr *local_ip, struct ip_addr *remote_ip, + u16_t local_port, u16_t remote_port); + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#else +# define tcp_debug_print(tcphdr) +# define tcp_debug_print_flags(flags) +# define tcp_debug_print_state(s) +# define tcp_debug_print_pcbs() +# define tcp_pcbs_sane() 1 +#endif /* TCP_DEBUG */ + +#if NO_SYS +#define tcp_timer_needed() +#else +void tcp_timer_needed(void); +#endif + +/* The TCP PCB lists. */ +union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; +}; +extern union tcp_listen_pcbs_t tcp_listen_pcbs; +extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */ + +/* Axioms about the above lists: + 1) Every TCP PCB that is not CLOSED is in one of the lists. + 2) A PCB is only in one of the lists. + 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state. + 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state. +*/ + +/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#if 0 +#define TCP_REG(pcbs, npcb) do {\ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", npcb, npcb->local_port)); \ + for(tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != npcb); \ + } \ + LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", npcb->state != CLOSED); \ + npcb->next = *pcbs; \ + LWIP_ASSERT("TCP_REG: npcb->next != npcb", npcb->next != npcb); \ + *(pcbs) = npcb; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + LWIP_ASSERT("TCP_RMV: pcbs != NULL", *pcbs != NULL); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", npcb, *pcbs)); \ + if(*pcbs == npcb) { \ + *pcbs = (*pcbs)->next; \ + } else for(tcp_tmp_pcb = *pcbs; tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next != NULL && tcp_tmp_pcb->next == npcb) { \ + tcp_tmp_pcb->next = npcb->next; \ + break; \ + } \ + } \ + npcb->next = NULL; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", npcb, *pcbs)); \ + } while(0) + +#else /* LWIP_DEBUG */ +#define TCP_REG(pcbs, npcb) do { \ + npcb->next = *pcbs; \ + *(pcbs) = npcb; \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + if(*(pcbs) == npcb) { \ + (*(pcbs)) = (*pcbs)->next; \ + } else for(tcp_tmp_pcb = *pcbs; tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next != NULL && tcp_tmp_pcb->next == npcb) { \ + tcp_tmp_pcb->next = npcb->next; \ + break; \ + } \ + } \ + npcb->next = NULL; \ + } while(0) +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/lab/net/lwip/include/lwip/tcpip.h b/lab/net/lwip/include/lwip/tcpip.h new file mode 100644 index 0000000..0d29564 --- /dev/null +++ b/lab/net/lwip/include/lwip/tcpip.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCPIP_H__ +#define __LWIP_TCPIP_H__ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" +#include "lwip/netifapi.h" +#include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_sem_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_sem_wait(lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_sem_signal(lock_tcpip_core) +#define TCPIP_APIMSG(m) tcpip_apimsg_lock(m) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#else +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG(m) tcpip_apimsg(m) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(m->conn->op_completed) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +void tcpip_init(void (* tcpip_init_done)(void *), void *arg); + +#if LWIP_NETCONN +err_t tcpip_apimsg(struct api_msg *apimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_apimsg_lock(struct api_msg *apimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + +err_t tcpip_input(struct pbuf *p, struct netif *inp); + +#if LWIP_NETIF_API +err_t tcpip_netifapi(struct netifapi_msg *netifapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +err_t tcpip_callback_with_block(void (*f)(void *ctx), void *ctx, u8_t block); +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +#define tcpip_untimeout(h, arg) tcpip_timeout(0xffffffff, h, arg) + +enum tcpip_msg_type { +#if LWIP_NETCONN + TCPIP_MSG_API, +#endif /* LWIP_NETCONN */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_TIMEOUT +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + sys_sem_t *sem; + union { +#if LWIP_NETCONN + struct api_msg *apimsg; +#endif /* LWIP_NETCONN */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ + struct { + struct pbuf *p; + struct netif *netif; + } inp; + struct { + void (*f)(void *ctx); + void *ctx; + } cb; + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* __LWIP_TCPIP_H__ */ diff --git a/lab/net/lwip/include/lwip/udp.h b/lab/net/lwip/include/lwip/udp.h new file mode 100644 index 0000000..d7b2a38 --- /dev/null +++ b/lab/net/lwip/include/lwip/udp.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_UDP_H__ +#define __LWIP_UDP_H__ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_HLEN 8 + +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct udp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ + PACK_STRUCT_FIELD(u16_t len); + PACK_STRUCT_FIELD(u16_t chksum); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define UDP_FLAGS_NOCHKSUM 0x01U +#define UDP_FLAGS_UDPLITE 0x02U +#define UDP_FLAGS_CONNECTED 0x04U + +struct udp_pcb { +/* Common members of all PCB types */ + IP_PCB; + +/* Protocol specific PCB members */ + + struct udp_pcb *next; + + u8_t flags; + /* ports are in host byte order */ + u16_t local_port, remote_port; + +#if LWIP_IGMP + /* outgoing network interface for multicast packets */ + struct ip_addr multicast_ip; +#endif /* LWIP_IGMP */ + +#if LWIP_UDPLITE + /* used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /* receive callback function + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ + void (* recv)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port); + /* user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for exernal reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; + +/* The following functions is the application layer interface to the + UDP code. */ +struct udp_pcb * udp_new (void); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + struct ip_addr *addr, + u16_t port), + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#define udp_flags(pcb) ((pcb)->flags) +#define udp_setflags(pcb, f) ((pcb)->flags = (f)) + +/* The following functions are the lower layer interface to UDP. */ +void udp_input (struct pbuf *p, struct netif *inp); + +#define udp_init() /* Compatibility define, not init needed. */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/lab/net/lwip/include/netif/etharp.h b/lab/net/lwip/include/netif/etharp.h new file mode 100644 index 0000000..2b76823 --- /dev/null +++ b/lab/net/lwip/include/netif/etharp.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __NETIF_ETHARP_H__ +#define __NETIF_ETHARP_H__ + +#include "lwip/opt.h" + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message */ +struct etharp_hdr { + PACK_STRUCT_FIELD(struct eth_hdr ethhdr); + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u16_t _hwlen_protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct eth_addr shwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ethip_hdr { + PACK_STRUCT_FIELD(struct eth_hdr eth); + PACK_STRUCT_FIELD(struct ip_hdr ip); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 +#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ +void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, + struct eth_addr **eth_ret, struct ip_addr **ip_ret); +void etharp_ip_input(struct netif *netif, struct pbuf *p); +void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, + struct pbuf *p); +err_t etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr); +err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr); + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const struct ip_addr *ipsrc_addr, + const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, + const u16_t opcode); +#endif /* LWIP_AUTOIP */ + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP */ + +#endif /* __NETIF_ARP_H__ */ diff --git a/lab/net/lwip/include/netif/loopif.h b/lab/net/lwip/include/netif/loopif.h new file mode 100644 index 0000000..304af4b --- /dev/null +++ b/lab/net/lwip/include/netif/loopif.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __NETIF_LOOPIF_H__ +#define __NETIF_LOOPIF_H__ + +#include "lwip/opt.h" +#include "lwip/netif.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +#define loopif_poll netif_poll +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ + +err_t loopif_init(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_LOOPIF_H__ */ diff --git a/lab/net/lwip/include/netif/ppp_oe.h b/lab/net/lwip/include/netif/ppp_oe.h new file mode 100644 index 0000000..3aa55ae --- /dev/null +++ b/lab/net/lwip/include/netif/ppp_oe.h @@ -0,0 +1,161 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +struct pppoe_softc; + + +void pppoe_init(void); + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +extern int pppoe_hdrlen; + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/lab/net/lwip/include/netif/slipif.h b/lab/net/lwip/include/netif/slipif.h new file mode 100644 index 0000000..aa08ada --- /dev/null +++ b/lab/net/lwip/include/netif/slipif.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __NETIF_SLIPIF_H__ +#define __NETIF_SLIPIF_H__ + +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lab/net/lwip/jos/arch/cc.h b/lab/net/lwip/jos/arch/cc.h new file mode 100644 index 0000000..75d2798 --- /dev/null +++ b/lab/net/lwip/jos/arch/cc.h @@ -0,0 +1,41 @@ +#ifndef LWIP_ARCH_CC_H +#define LWIP_ARCH_CC_H + +#include +#include + +typedef uint32_t u32_t; +typedef int32_t s32_t; + +typedef uint64_t u64_t; +typedef int64_t s64_t; + +typedef uint16_t u16_t; +typedef int16_t s16_t; + +typedef uint8_t u8_t; +typedef int8_t s8_t; + +typedef uintptr_t mem_ptr_t; + +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_END + +#define S16_F "d" +#define U16_F "u" +#define X16_F "x" + +#define S32_F "d" +#define U32_F "u" +#define X32_F "x" + +#define LWIP_PLATFORM_DIAG(x) cprintf x +#define LWIP_PLATFORM_ASSERT(x) panic(x) + +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif + +#endif diff --git a/lab/net/lwip/jos/arch/i386/setjmp.h b/lab/net/lwip/jos/arch/i386/setjmp.h new file mode 100644 index 0000000..4f24a91 --- /dev/null +++ b/lab/net/lwip/jos/arch/i386/setjmp.h @@ -0,0 +1,17 @@ +#ifndef JOS_MACHINE_SETJMP_H +#define JOS_MACHINE_SETJMP_H + +#include + +#define JOS_LONGJMP_GCCATTR regparm(2) + +struct jos_jmp_buf { + uint32_t jb_eip; + uint32_t jb_esp; + uint32_t jb_ebp; + uint32_t jb_ebx; + uint32_t jb_esi; + uint32_t jb_edi; +}; + +#endif diff --git a/lab/net/lwip/jos/arch/longjmp.S b/lab/net/lwip/jos/arch/longjmp.S new file mode 100644 index 0000000..5fa3942 --- /dev/null +++ b/lab/net/lwip/jos/arch/longjmp.S @@ -0,0 +1,39 @@ +#ifndef _ALIGN_TEXT +#define _ALIGN_TEXT .align 16, 0x90 +#endif + +#define ENTRY(x) \ + .text; _ALIGN_TEXT; .globl x; .type x,@function; x: + + +ENTRY(jos_setjmp) + movl 4(%esp), %ecx // jos_jmp_buf + + movl 0(%esp), %edx // %eip as pushed by call + movl %edx, 0(%ecx) + + leal 4(%esp), %edx // where %esp will point when we return + movl %edx, 4(%ecx) + + movl %ebp, 8(%ecx) + movl %ebx, 12(%ecx) + movl %esi, 16(%ecx) + movl %edi, 20(%ecx) + + movl $0, %eax + ret + +ENTRY(jos_longjmp) + // %eax is the jos_jmp_buf* + // %edx is the return value + + movl 0(%eax), %ecx // %eip + movl 4(%eax), %esp + movl 8(%eax), %ebp + movl 12(%eax), %ebx + movl 16(%eax), %esi + movl 20(%eax), %edi + + movl %edx, %eax + jmp *%ecx + diff --git a/lab/net/lwip/jos/arch/perf.h b/lab/net/lwip/jos/arch/perf.h new file mode 100644 index 0000000..d1adb97 --- /dev/null +++ b/lab/net/lwip/jos/arch/perf.h @@ -0,0 +1,7 @@ +#ifndef LWIP_ARCH_PERF_H +#define LWIP_ARCH_PERF_H + +#define PERF_START +#define PERF_STOP(x) + +#endif diff --git a/lab/net/lwip/jos/arch/perror.c b/lab/net/lwip/jos/arch/perror.c new file mode 100644 index 0000000..0e25a1b --- /dev/null +++ b/lab/net/lwip/jos/arch/perror.c @@ -0,0 +1,166 @@ + +#define LWIP_PROVIDE_ERRNO +#include +#include + +const char *sys_errlist[] = { + [EPERM] = "EPERM", /* Operation not permitted */ + [ENOENT] = "ENOENT", /* No such file or directory */ + [ESRCH] = "ESRCH", /* No such process */ + [EINTR] = "EINTR", /* Interrupted system call */ + [EIO] = "EIO", /* I/O error */ + [ENXIO] = "ENXIO", /* No such device or address */ + [E2BIG] = "E2BIG", /* Arg list too long */ + [ENOEXEC] = "ENOEXEC", /* Exec format error */ + [EBADF] = "EBADF", /* Bad file number */ + [ECHILD] = "ECHILD", /* No child processes */ + [EAGAIN] = "EAGAIN", /* Try again */ + [ENOMEM] = "ENOMEM", /* Out of memory */ + [EACCES] = "EACCES", /* Permission denied */ + [EFAULT] = "EFAULT", /* Bad address */ + [ENOTBLK] = "ENOTBLK", /* Block device required */ + [EBUSY] = "EBUSY", /* Device or resource busy */ + [EEXIST] = "EEXIST", /* File exists */ + [EXDEV] = "EXDEV", /* Cross-device link */ + [ENODEV] = "ENODEV", /* No such device */ + [ENOTDIR] = "ENOTDIR", /* Not a directory */ + [EISDIR] = "EISDIR", /* Is a directory */ + [EINVAL] = "EINVAL", /* Invalid argument */ + [ENFILE] = "ENFILE", /* File table overflow */ + [EMFILE] = "EMFILE", /* Too many open files */ + [ENOTTY] = "ENOTTY", /* Not a typewriter */ + [ETXTBSY] = "ETXTBSY", /* Text file busy */ + [EFBIG] = "EFBIG", /* File too large */ + [ENOSPC] = "ENOSPC", /* No space left on device */ + [ESPIPE] = "ESPIPE", /* Illegal seek */ + [EROFS] = "EROFS", /* Read-only file system */ + [EMLINK] = "EMLINK", /* Too many links */ + [EPIPE] = "EPIPE", /* Broken pipe */ + [EDOM] = "EDOM", /* Math argument out of domain of func */ + [ERANGE] = "ERANGE", /* Math result not representable */ + [EDEADLK] = "EDEADLK", /* Resource deadlock would occur */ + [ENAMETOOLONG] = "ENAMETOOLONG", /* File name too long */ + [ENOLCK] = "ENOLCK", /* No record locks available */ + [ENOSYS] = "ENOSYS", /* Function not implemented */ + [ENOTEMPTY] = "ENOTEMPTY", /* Directory not empty */ + [ELOOP] = "ELOOP", /* Too many symbolic links encountered */ + [EWOULDBLOCK] = "EWOULDBLOCK", /* Operation would block */ + [ENOMSG] = "ENOMSG", /* No message of desired type */ + [EIDRM] = "EIDRM", /* Identifier removed */ + [ECHRNG] = "ECHRNG", /* Channel number out of range */ + [EL2NSYNC] = "EL2NSYNC", /* Level 2 not synchronized */ + [EL3HLT] = "EL3HLT", /* Level 3 halted */ + [EL3RST] = "EL3RST", /* Level 3 reset */ + [ELNRNG] = "ELNRNG", /* Link number out of range */ + [EUNATCH] = "EUNATCH", /* Protocol driver not attached */ + [ENOCSI] = "ENOCSI", /* No CSI structure available */ + [EL2HLT] = "EL2HLT", /* Level 2 halted */ + [EBADE] = "EBADE", /* Invalid exchange */ + [EBADR] = "EBADR", /* Invalid request descriptor */ + [EXFULL] = "EXFULL", /* Exchange full */ + [ENOANO] = "ENOANO", /* No anode */ + [EBADRQC] = "EBADRQC", /* Invalid request code */ + [EBADSLT] = "EBADSLT", /* Invalid slot */ + + [EDEADLOCK] = "EDEADLOCK", + + [EBFONT] = "EBFONT", /* Bad font file format */ + [ENOSTR] = "ENOSTR", /* Device not a stream */ + [ENODATA] = "ENODATA", /* No data available */ + [ETIME] = "ETIME", /* Timer expired */ + [ENOSR] = "ENOSR", /* Out of streams resources */ + [ENONET] = "ENONET", /* Machine is not on the network */ + [ENOPKG] = "ENOPKG", /* Package not installed */ + [EREMOTE] = "EREMOTE", /* Object is remote */ + [ENOLINK] = "ENOLINK", /* Link has been severed */ + [EADV] = "EADV", /* Advertise error */ + [ESRMNT] = "ESRMNT", /* Srmount error */ + [ECOMM] = "ECOMM", /* Communication error on send */ + [EPROTO] = "EPROTO", /* Protocol error */ + [EMULTIHOP] = "EMULTIHOP", /* Multihop attempted */ + [EDOTDOT] = "EDOTDOT", /* RFS specific error */ + [EBADMSG] = "EBADMSG", /* Not a data message */ + [EOVERFLOW] = "EOVERFLOW", /* Value too large for defined data type */ + [ENOTUNIQ] = "ENOTUNIQ", /* Name not unique on network */ + [EBADFD] = "EBADFD", /* File descriptor in bad state */ + [EREMCHG] = "EREMCHG", /* Remote address changed */ + [ELIBACC] = "ELIBACC", /* Can not access a needed shared library */ + [ELIBBAD] = "ELIBBAD", /* Accessing a corrupted shared library */ + [ELIBSCN] = "ELIBSCN", /* .lib section in a.out corrupted */ + [ELIBMAX] = "ELIBMAX", /* Attempting to link in too many shared libraries */ + [ELIBEXEC] = "ELIBEXEC", /* Cannot exec a shared library directly */ + [EILSEQ] = "EILSEQ", /* Illegal byte sequence */ + [ERESTART] = "ERESTART", /* Interrupted system call should be restarted */ + [ESTRPIPE] = "ESTRPIPE", /* Streams pipe error */ + [EUSERS] = "EUSERS", /* Too many users */ + [ENOTSOCK] = "ENOTSOCK", /* Socket operation on non-socket */ + [EDESTADDRREQ] = "EDESTADDRREQ", /* Destination address required */ + [EMSGSIZE] = "EMSGSIZE", /* Message too long */ + [EPROTOTYPE] = "EPROTOTYPE", /* Protocol wrong type for socket */ + [ENOPROTOOPT] = "ENOPROTOOPT", /* Protocol not available */ + [EPROTONOSUPPORT] = "EPROTONOSUPPORT", /* Protocol not supported */ + [ESOCKTNOSUPPORT] = "ESOCKTNOSUPPORT", /* Socket type not supported */ + [EOPNOTSUPP] = "EOPNOTSUPP", /* Operation not supported on transport endpoint */ + [EPFNOSUPPORT] = "EPFNOSUPPORT", /* Protocol family not supported */ + [EAFNOSUPPORT] = "EAFNOSUPPORT", /* Address family not supported by protocol */ + [EADDRINUSE] = "EADDRINUSE", /* Address already in use */ + [EADDRNOTAVAIL] = "EADDRNOTAVAIL", /* Cannot assign requested address */ + [ENETDOWN] = "ENETDOWN", /* Network is down */ + [ENETUNREACH] = "ENETUNREACH", /* Network is unreachable */ + [ENETRESET] = "ENETRESET", /* Network dropped connection because of reset */ + [ECONNABORTED] = "ECONNABORTED", /* Software caused connection abort */ + [ECONNRESET] = "ECONNRESET", /* Connection reset by peer */ + [ENOBUFS] = "ENOBUFS", /* No buffer space available */ + [EISCONN] = "EISCONN", /* Transport endpoint is already connected */ + [ENOTCONN] = "ENOTCONN", /* Transport endpoint is not connected */ + [ESHUTDOWN] = "ESHUTDOWN", /* Cannot send after transport endpoint shutdown */ + [ETOOMANYREFS] = "ETOOMANYREFS", /* Too many references: cannot splice */ + [ETIMEDOUT] = "ETIMEDOUT", /* Connection timed out */ + [ECONNREFUSED] = "ECONNREFUSED", /* Connection refused */ + [EHOSTDOWN] = "EHOSTDOWN", /* Host is down */ + [EHOSTUNREACH] = "EHOSTUNREACH", /* No route to host */ + [EALREADY] = "EALREADY", /* Operation already in progress */ + [EINPROGRESS] = "EINPROGRESS", /* Operation now in progress */ + [ESTALE] = "ESTALE", /* Stale NFS file handle */ + [EUCLEAN] = "EUCLEAN", /* Structure needs cleaning */ + [ENOTNAM] = "ENOTNAM", /* Not a XENIX named type file */ + [ENAVAIL] = "ENAVAIL", /* No XENIX semaphores available */ + [EISNAM] = "EISNAM", /* Is a named type file */ + [EREMOTEIO] = "EREMOTEIO", /* Remote I/O error */ + [EDQUOT] = "EDQUOT", /* Quota exceeded */ + + [ENOMEDIUM] = "ENOMEDIUM", /* No medium found */ + [EMEDIUMTYPE] = "EMEDIUMTYPE", /* Wrong medium type */ + + + [ENSROK] = "ENSROK", /* DNS server returned answer with no data */ + [ENSRNODATA] = "ENSRNODATA", /* DNS server returned answer with no data */ + [ENSRFORMERR] = "ENSRFORMERR", /* DNS server claims query was misformatted */ + [ENSRSERVFAIL] = "ENSRSERVFAIL", /* DNS server returned general failure */ + [ENSRNOTFOUND] = "ENSRNOTFOUND", /* Domain name not found */ + [ENSRNOTIMP] = "ENSRNOTIMP", /* DNS server does not implement requested operation */ + [ENSRREFUSED] = "ENSRREFUSED", /* DNS server refused query */ + [ENSRBADQUERY] = "ENSRBADQUERY", /* Misformatted DNS query */ + [ENSRBADNAME] = "ENSRBADNAME", /* Misformatted domain name */ + [ENSRBADFAMILY] = "ENSRBADFAMILY", /* Unsupported address family */ + [ENSRBADRESP] = "ENSRBADRESP", /* Misformatted DNS reply */ + [ENSRCONNREFUSED] = "ENSRCONNREFUSED", /* Could not contact DNS servers */ + [ENSRTIMEOUT] = "ENSRTIMEOUT", /* Timeout while contacting DNS servers */ + [ENSROF] = "ENSROF", /* End of file */ + [ENSRFILE] = "ENSRFILE", /* Error reading file */ + [ENSRNOMEM] = "ENSRNOMEM", /* Out of memory */ + [ENSRDESTRUCTION] = "ENSRDESTRUCTION", /* Application terminated lookup */ + [ENSRQUERYDOMAINTOOLONG] = "ENSRQUERYDOMAINTOOLONG", /* Domain name is too long */ + [ENSRCNAMELOOP] = "ENSRCNAMELOOP" /* Domain name is too long */ +}; + +void +perror(const char *s) { + int err = errno; + cprintf("%s: %s\n", s, e2s(err)); +} + +const char * +e2s(int err) { + return sys_errlist[err]; +} diff --git a/lab/net/lwip/jos/arch/perror.h b/lab/net/lwip/jos/arch/perror.h new file mode 100644 index 0000000..bb7be80 --- /dev/null +++ b/lab/net/lwip/jos/arch/perror.h @@ -0,0 +1,7 @@ +#ifndef JOS_INC_PERROR_H +#define JOS_INC_PERROR_H + +void perror(const char *s); +const char *e2s(int e); + +#endif /* !JOS_INC_PERROR_H */ diff --git a/lab/net/lwip/jos/arch/queue.h b/lab/net/lwip/jos/arch/queue.h new file mode 100644 index 0000000..b2c7222 --- /dev/null +++ b/lab/net/lwip/jos/arch/queue.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + * + * For Jos, extra comments have been added to this file, and the original + * TAILQ and CIRCLEQ definitions have been removed. - August 9, 2005 + */ + +#ifndef LWIP_ARCH_QUEUE_H +#define LWIP_ARCH_QUEUE_H + +/* + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + */ + +/* + * An example using the below functions. + */ +#if 0 + +struct Frob +{ + int frobozz; + LIST_ENTRY(Frob) frob_link; /* this contains the list element pointers */ +}; + +LIST_HEAD(Frob_list, Frob) /* defines struct Frob_list as a list of Frob */ + +struct Frob_list flist; /* declare a Frob list */ + +LIST_INIT(&flist); /* clear flist (globals are cleared anyway) */ +flist = LIST_HEAD_INITIALIZER(&flist); /* alternate way to clear flist */ + +if(LIST_EMPTY(&flist)) /* check whether list is empty */ + printf("list is empty\n"); + +struct Frob *f = LIST_FIRST(&flist); /* f is first element in list */ +f = LIST_NEXT(f, frob_link); /* now f is next (second) element in list */ +f = LIST_NEXT(f, frob_link); /* now f is next (third) element in list */ + +for(f=LIST_FIRST(&flist); f != 0; /* iterate over elements in flist */ + f = LIST_NEXT(f, frob_link)) + printf("f %d\n", f->frobozz); + +LIST_FOREACH(f, &flist, frob_link) /* alternate way to say that */ + printf("f %d\n", f->frobozz); + +f = LIST_NEXT(LIST_FIRST(&flist)); /* f is second element in list */ +LIST_INSERT_AFTER(f, g, frob_link); /* add g right after f in list */ +LIST_REMOVE(g, frob_link); /* remove g from list (can't insert twice!) */ +LIST_INSERT_BEFORE(f, g, frob_link); /* add g right before f */ +LIST_REMOVE(g, frob_link); /* remove g again */ +LIST_INSERT_HEAD(&flist, g, frob_link); /* add g as first element in list */ + +#endif + +/* + * List declarations. + */ + +/* + * A list is headed by a structure defined by the LIST_HEAD macro. This structure con‐ + * tains a single pointer to the first element on the list. The elements are doubly + * linked so that an arbitrary element can be removed without traversing the list. New + * elements can be added to the list after an existing element or at the head of the list. + * A LIST_HEAD structure is declared as follows: + * + * LIST_HEAD(HEADNAME, TYPE) head; + * + * where HEADNAME is the name of the structure to be defined, and TYPE is the type of the + * elements to be linked into the list. A pointer to the head of the list can later be + * declared as: + * + * struct HEADNAME *headp; + * + * (The names head and headp are user selectable.) + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +/* + * Set a list head variable to LIST_HEAD_INITIALIZER(head) + * to reset it to the empty list. + */ +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +/* + * Use this inside a structure "LIST_ENTRY(type) field" to use + * x as the list piece. + * + * The le_prev points at the pointer to the structure containing + * this very LIST_ENTRY, so that if we want to remove this list entry, + * we can do *le_prev = le_next to update the structure pointing at us. + */ +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* ptr to ptr to this element */ \ +} + +/* + * List functions. + */ + +/* + * Is the list named "head" empty? + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +/* + * Return the first element in the list named "head". + */ +#define LIST_FIRST(head) ((head)->lh_first) + +/* + * Return the element after "elm" in the list. + * The "field" name is the link element as above. + */ +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +/* + * Iterate over the elements in the list named "head". + * During the loop, assign the list elements to the variable "var" + * and use the LIST_ENTRY structure member "field" as the link field. + */ +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +/* + * Reset the list named "head" to the empty list. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +/* + * Insert the element "elm" *after* the element "listelm" which is + * already in the list. The "field" name is the link element + * as above. + */ +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +/* + * Insert the element "elm" *before* the element "listelm" which is + * already in the list. The "field" name is the link element + * as above. + */ +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +/* + * Insert the element "elm" at the head of the list named "head". + * The "field" name is the link element as above. + */ +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +/* + * Remove the element "elm" from the list. + * The "field" name is the link element as above. + */ +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/lab/net/lwip/jos/arch/setjmp.h b/lab/net/lwip/jos/arch/setjmp.h new file mode 100644 index 0000000..de0d08a --- /dev/null +++ b/lab/net/lwip/jos/arch/setjmp.h @@ -0,0 +1,10 @@ +#ifndef JOS_INC_SETJMP_H +#define JOS_INC_SETJMP_H + +#include + +int jos_setjmp(volatile struct jos_jmp_buf *buf); +void jos_longjmp(volatile struct jos_jmp_buf *buf, int val) + __attribute__((__noreturn__, JOS_LONGJMP_GCCATTR)); + +#endif diff --git a/lab/net/lwip/jos/arch/sys_arch.c b/lab/net/lwip/jos/arch/sys_arch.c new file mode 100644 index 0000000..00c501c --- /dev/null +++ b/lab/net/lwip/jos/arch/sys_arch.c @@ -0,0 +1,322 @@ +#include + +#include +#include +#include +#include +#include +#include + +#define debug 0 + +#define NSEM 256 +#define NMBOX 128 +#define MBOXSLOTS 32 + +struct sys_sem_entry { + int freed; + int gen; + union { + uint32_t v; + struct { + uint16_t counter; + uint16_t waiters; + }; + }; + LIST_ENTRY(sys_sem_entry) link; +}; +static struct sys_sem_entry sems[NSEM]; +static LIST_HEAD(sem_list, sys_sem_entry) sem_free; + +struct sys_mbox_entry { + int freed; + int head, nextq; + void *msg[MBOXSLOTS]; + sys_sem_t queued_msg; + sys_sem_t free_msg; + LIST_ENTRY(sys_mbox_entry) link; +}; +static struct sys_mbox_entry mboxes[NMBOX]; +static LIST_HEAD(mbox_list, sys_mbox_entry) mbox_free; + +struct sys_thread { + thread_id_t tid; + struct sys_timeouts tmo; + LIST_ENTRY(sys_thread) link; +}; + +enum { thread_hash_size = 257 }; +static LIST_HEAD(thread_list, sys_thread) threads[thread_hash_size]; + +void +sys_init(void) +{ + int i = 0; + for (i = 0; i < NSEM; i++) { + sems[i].freed = 1; + LIST_INSERT_HEAD(&sem_free, &sems[i], link); + } + + for (i = 0; i < NMBOX; i++) { + mboxes[i].freed = 1; + LIST_INSERT_HEAD(&mbox_free, &mboxes[i], link); + } +} + +sys_mbox_t +sys_mbox_new(int size) +{ + assert(size < MBOXSLOTS); + struct sys_mbox_entry *mbe = LIST_FIRST(&mbox_free); + if (!mbe) { + cprintf("lwip: sys_mbox_new: out of mailboxes\n"); + return SYS_MBOX_NULL; + } + LIST_REMOVE(mbe, link); + assert(mbe->freed); + mbe->freed = 0; + + int i = mbe - &mboxes[0]; + mbe->head = -1; + mbe->nextq = 0; + mbe->queued_msg = sys_sem_new(0); + mbe->free_msg = sys_sem_new(MBOXSLOTS); + + if (mbe->queued_msg == SYS_SEM_NULL || + mbe->free_msg == SYS_SEM_NULL) + { + sys_mbox_free(i); + cprintf("lwip: sys_mbox_new: can't get semaphore\n"); + return SYS_MBOX_NULL; + } + return i; +} + +void +sys_mbox_free(sys_mbox_t mbox) +{ + assert(!mboxes[mbox].freed); + sys_sem_free(mboxes[mbox].queued_msg); + sys_sem_free(mboxes[mbox].free_msg); + LIST_INSERT_HEAD(&mbox_free, &mboxes[mbox], link); + mboxes[mbox].freed = 1; +} + +void +sys_mbox_post(sys_mbox_t mbox, void *msg) +{ + assert(sys_mbox_trypost(mbox, msg) == ERR_OK); +} + +err_t +sys_mbox_trypost(sys_mbox_t mbox, void *msg) +{ + assert(!mboxes[mbox].freed); + + sys_arch_sem_wait(mboxes[mbox].free_msg, 0); + if (mboxes[mbox].nextq == mboxes[mbox].head) + return ERR_MEM; + + int slot = mboxes[mbox].nextq; + mboxes[mbox].nextq = (slot + 1) % MBOXSLOTS; + mboxes[mbox].msg[slot] = msg; + + if (mboxes[mbox].head == -1) + mboxes[mbox].head = slot; + + sys_sem_signal(mboxes[mbox].queued_msg); + + return ERR_OK; +} + +sys_sem_t +sys_sem_new(u8_t count) +{ + struct sys_sem_entry *se = LIST_FIRST(&sem_free); + if (!se) { + cprintf("lwip: sys_sem_new: out of semaphores\n"); + return SYS_SEM_NULL; + } + LIST_REMOVE(se, link); + assert(se->freed); + se->freed = 0; + + se->counter = count; + se->gen++; + return se - &sems[0]; +} + +void +sys_sem_free(sys_sem_t sem) +{ + assert(!sems[sem].freed); + sems[sem].freed = 1; + sems[sem].gen++; + LIST_INSERT_HEAD(&sem_free, &sems[sem], link); +} + +void +sys_sem_signal(sys_sem_t sem) +{ + assert(!sems[sem].freed); + sems[sem].counter++; + if (sems[sem].waiters) { + sems[sem].waiters = 0; + thread_wakeup(&sems[sem].v); + } +} + +u32_t +sys_arch_sem_wait(sys_sem_t sem, u32_t tm_msec) +{ + assert(!sems[sem].freed); + u32_t waited = 0; + + int gen = sems[sem].gen; + + while (tm_msec == 0 || waited < tm_msec) { + if (sems[sem].counter > 0) { + sems[sem].counter--; + return waited; + } else if (tm_msec == SYS_ARCH_NOWAIT) { + return SYS_ARCH_TIMEOUT; + } else { + uint32_t a = sys_time_msec(); + uint32_t sleep_until = tm_msec ? a + (tm_msec - waited) : ~0; + sems[sem].waiters = 1; + uint32_t cur_v = sems[sem].v; + lwip_core_unlock(); + thread_wait(&sems[sem].v, cur_v, sleep_until); + lwip_core_lock(); + if (gen != sems[sem].gen) { + cprintf("sys_arch_sem_wait: sem freed under waiter!\n"); + return SYS_ARCH_TIMEOUT; + } + uint32_t b = sys_time_msec(); + waited += (b - a); + } + } + + return SYS_ARCH_TIMEOUT; +} + +u32_t +sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t tm_msec) +{ + assert(!mboxes[mbox].freed); + + u32_t waited = sys_arch_sem_wait(mboxes[mbox].queued_msg, tm_msec); + if (waited == SYS_ARCH_TIMEOUT) + return waited; + + int slot = mboxes[mbox].head; + if (slot == -1) + panic("lwip: sys_arch_mbox_fetch: no message"); + if (msg) + *msg = mboxes[mbox].msg[slot]; + + mboxes[mbox].head = (slot + 1) % MBOXSLOTS; + if (mboxes[mbox].head == mboxes[mbox].nextq) + mboxes[mbox].head = -1; + + sys_sem_signal(mboxes[mbox].free_msg); + return waited; +} + +u32_t +sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) +{ + return sys_arch_mbox_fetch(mbox, msg, SYS_ARCH_NOWAIT); +} + +struct lwip_thread { + void (*func)(void *arg); + void *arg; +}; + +static void +lwip_thread_entry(uint32_t arg) +{ + struct lwip_thread *lt = (struct lwip_thread *)arg; + lwip_core_lock(); + lt->func(lt->arg); + lwip_core_unlock(); + free(lt); +} + +sys_thread_t +sys_thread_new(char *name, void (* thread)(void *arg), void *arg, + int stacksize, int prio) +{ + struct lwip_thread *lt = malloc(sizeof(*lt)); + if (lt == 0) + panic("sys_thread_new: cannot allocate thread struct"); + + if (stacksize > PGSIZE) + panic("large stack %d", stacksize); + + lt->func = thread; + lt->arg = arg; + + thread_id_t tid; + int r = thread_create(&tid, name, lwip_thread_entry, (uint32_t)lt); + + if (r < 0) + panic("lwip: sys_thread_new: cannot create: %s\n", e2s(r)); + + return tid; +} + +static void +timeout_cleanup(thread_id_t tid) +{ + lwip_core_lock(); + + struct sys_thread *t; + LIST_FOREACH(t, &threads[tid % thread_hash_size], link) + if (t->tid == tid) { + LIST_REMOVE(t, link); + free(t); + goto done; + } + + if (debug) cprintf("timeout_cleanup: bogus tid %ld\n", tid); + done: + lwip_core_unlock(); +} + +struct sys_timeouts * +sys_arch_timeouts(void) +{ + thread_id_t tid = thread_id(); + + struct sys_thread *t; + LIST_FOREACH(t, &threads[tid % thread_hash_size], link) + if (t->tid == tid) + goto out; + + t = malloc(sizeof(*t)); + if (t == 0) + panic("sys_arch_timeouts: cannot malloc"); + + int r = thread_onhalt(timeout_cleanup); + if (r < 0) + panic("thread_onhalt failed: %s", e2s(r)); + + t->tid = tid; + memset(&t->tmo, 0, sizeof(t->tmo)); + LIST_INSERT_HEAD(&threads[tid % thread_hash_size], t, link); + +out: + return &t->tmo; +} + +void +lwip_core_lock(void) +{ +} + +void +lwip_core_unlock(void) +{ +} diff --git a/lab/net/lwip/jos/arch/sys_arch.h b/lab/net/lwip/jos/arch/sys_arch.h new file mode 100644 index 0000000..7bcbd17 --- /dev/null +++ b/lab/net/lwip/jos/arch/sys_arch.h @@ -0,0 +1,23 @@ +#ifndef LWIP_ARCH_SYS_ARCH_H +#define LWIP_ARCH_SYS_ARCH_H + +#include + +typedef int sys_sem_t; +typedef int sys_mbox_t; +typedef int sys_thread_t; + +#define SYS_MBOX_NULL (-1) +#define SYS_SEM_NULL (-1) + +void lwip_core_lock(void); +void lwip_core_unlock(void); +void lwip_core_init(void); + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#define SYS_ARCH_NOWAIT 0xfffffffe + +#endif diff --git a/lab/net/lwip/jos/arch/thread.c b/lab/net/lwip/jos/arch/thread.c new file mode 100644 index 0000000..3aaba40 --- /dev/null +++ b/lab/net/lwip/jos/arch/thread.c @@ -0,0 +1,188 @@ +#include + +#include +#include +#include + +static thread_id_t max_tid; +static struct thread_context *cur_tc; + +static struct thread_queue thread_queue; +static struct thread_queue kill_queue; + +void +thread_init(void) { + threadq_init(&thread_queue); + max_tid = 0; +} + +uint32_t +thread_id(void) { + return cur_tc->tc_tid; +} + +void +thread_wakeup(volatile uint32_t *addr) { + struct thread_context *tc = thread_queue.tq_first; + while (tc) { + if (tc->tc_wait_addr == addr) + tc->tc_wakeup = 1; + tc = tc->tc_queue_link; + } +} + +void +thread_wait(volatile uint32_t *addr, uint32_t val, uint32_t msec) { + uint32_t s = sys_time_msec(); + uint32_t p = s; + + cur_tc->tc_wait_addr = addr; + cur_tc->tc_wakeup = 0; + + while (p < msec) { + if (p < s) + break; + if (addr && *addr != val) + break; + if (cur_tc->tc_wakeup) + break; + + thread_yield(); + p = sys_time_msec(); + } + + cur_tc->tc_wait_addr = 0; + cur_tc->tc_wakeup = 0; +} + +int +thread_wakeups_pending(void) +{ + struct thread_context *tc = thread_queue.tq_first; + int n = 0; + while (tc) { + if (tc->tc_wakeup) + ++n; + tc = tc->tc_queue_link; + } + return n; +} + +int +thread_onhalt(void (*fun)(thread_id_t)) { + if (cur_tc->tc_nonhalt >= THREAD_NUM_ONHALT) + return -E_NO_MEM; + + cur_tc->tc_onhalt[cur_tc->tc_nonhalt++] = fun; + return 0; +} + +static thread_id_t +alloc_tid(void) { + int tid = max_tid++; + if (max_tid == (uint32_t)~0) + panic("alloc_tid: no more thread ids"); + return tid; +} + +static void +thread_set_name(struct thread_context *tc, const char *name) +{ + strncpy(tc->tc_name, name, name_size - 1); + tc->tc_name[name_size - 1] = 0; +} + +static void +thread_entry(void) { + cur_tc->tc_entry(cur_tc->tc_arg); + thread_halt(); +} + +int +thread_create(thread_id_t *tid, const char *name, + void (*entry)(uint32_t), uint32_t arg) { + struct thread_context *tc = malloc(sizeof(struct thread_context)); + if (!tc) + return -E_NO_MEM; + + memset(tc, 0, sizeof(struct thread_context)); + + thread_set_name(tc, name); + tc->tc_tid = alloc_tid(); + + tc->tc_stack_bottom = malloc(stack_size); + if (!tc->tc_stack_bottom) { + free(tc); + return -E_NO_MEM; + } + + void *stacktop = tc->tc_stack_bottom + stack_size; + // Terminate stack unwinding + stacktop = stacktop - 4; + memset(stacktop, 0, 4); + + memset(&tc->tc_jb, 0, sizeof(tc->tc_jb)); + tc->tc_jb.jb_esp = (uint32_t)stacktop; + tc->tc_jb.jb_eip = (uint32_t)&thread_entry; + tc->tc_entry = entry; + tc->tc_arg = arg; + + threadq_push(&thread_queue, tc); + + if (tid) + *tid = tc->tc_tid; + return 0; +} + +static void +thread_clean(struct thread_context *tc) { + if (!tc) return; + + int i; + for (i = 0; i < tc->tc_nonhalt; i++) + tc->tc_onhalt[i](tc->tc_tid); + free(tc->tc_stack_bottom); + free(tc); +} + +void +thread_halt() { + // right now the kill_queue will never be more than one + // clean up a thread if one is on the queue + thread_clean(threadq_pop(&kill_queue)); + + threadq_push(&kill_queue, cur_tc); + cur_tc = NULL; + thread_yield(); + // WHAT IF THERE ARE NO MORE THREADS? HOW DO WE STOP? + // when yield has no thread to run, it will return here! + exit(); +} + +void +thread_yield(void) { + struct thread_context *next_tc = threadq_pop(&thread_queue); + + if (!next_tc) + return; + + if (cur_tc) { + if (jos_setjmp(&cur_tc->tc_jb) != 0) + return; + threadq_push(&thread_queue, cur_tc); + } + + cur_tc = next_tc; + jos_longjmp(&cur_tc->tc_jb, 1); +} + +static void +print_jb(struct thread_context *tc) { + cprintf("jump buffer for thread %s:\n", tc->tc_name); + cprintf("\teip: %x\n", tc->tc_jb.jb_eip); + cprintf("\tesp: %x\n", tc->tc_jb.jb_esp); + cprintf("\tebp: %x\n", tc->tc_jb.jb_ebp); + cprintf("\tebx: %x\n", tc->tc_jb.jb_ebx); + cprintf("\tesi: %x\n", tc->tc_jb.jb_esi); + cprintf("\tedi: %x\n", tc->tc_jb.jb_edi); +} diff --git a/lab/net/lwip/jos/arch/thread.h b/lab/net/lwip/jos/arch/thread.h new file mode 100644 index 0000000..d4ab570 --- /dev/null +++ b/lab/net/lwip/jos/arch/thread.h @@ -0,0 +1,19 @@ +#ifndef LWIP_ARCH_THREAD_H +#define LWIP_ARCH_THREAD_H + +#include + +typedef uint32_t thread_id_t; + +void thread_init(void); +thread_id_t thread_id(void); +void thread_wakeup(volatile uint32_t *addr); +void thread_wait(volatile uint32_t *addr, uint32_t val, uint32_t msec); +int thread_wakeups_pending(void); +int thread_onhalt(void (*fun)(thread_id_t)); +int thread_create(thread_id_t *tid, const char *name, + void (*entry)(uint32_t), uint32_t arg); +void thread_yield(void); +void thread_halt(void); + +#endif diff --git a/lab/net/lwip/jos/arch/threadq.h b/lab/net/lwip/jos/arch/threadq.h new file mode 100644 index 0000000..72c42be --- /dev/null +++ b/lab/net/lwip/jos/arch/threadq.h @@ -0,0 +1,65 @@ +#ifndef JOS_INC_THREADQ_H +#define JOS_INC_THREADQ_H + +#include +#include + +#define THREAD_NUM_ONHALT 4 +enum { name_size = 32 }; +enum { stack_size = PGSIZE }; + +struct thread_context; + +struct thread_queue +{ + struct thread_context *tq_first; + struct thread_context *tq_last; +}; + +struct thread_context { + thread_id_t tc_tid; + void *tc_stack_bottom; + char tc_name[name_size]; + void (*tc_entry)(uint32_t); + uint32_t tc_arg; + struct jos_jmp_buf tc_jb; + volatile uint32_t *tc_wait_addr; + volatile char tc_wakeup; + void (*tc_onhalt[THREAD_NUM_ONHALT])(thread_id_t); + int tc_nonhalt; + struct thread_context *tc_queue_link; +}; + +static inline void +threadq_init(struct thread_queue *tq) +{ + tq->tq_first = 0; + tq->tq_last = 0; +} + +static inline void +threadq_push(struct thread_queue *tq, struct thread_context *tc) +{ + tc->tc_queue_link = 0; + if (!tq->tq_first) { + tq->tq_first = tc; + tq->tq_last = tc; + } else { + tq->tq_last->tc_queue_link = tc; + tq->tq_last = tc; + } +} + +static inline struct thread_context * +threadq_pop(struct thread_queue *tq) +{ + if (!tq->tq_first) + return 0; + + struct thread_context *tc = tq->tq_first; + tq->tq_first = tc->tc_queue_link; + tc->tc_queue_link = 0; + return tc; +} + +#endif diff --git a/lab/net/lwip/jos/jif/jif.c b/lab/net/lwip/jos/jif/jif.c new file mode 100644 index 0000000..54f89ab --- /dev/null +++ b/lab/net/lwip/jos/jif/jif.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include +#include + +#include + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include + +#include + +#define PKTMAP 0x10000000 + +struct jif { + struct eth_addr *ethaddr; + envid_t envid; +}; + +static void +low_level_init(struct netif *netif) +{ + int r; + + netif->hwaddr_len = 6; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST; + + // MAC address is hardcoded to eliminate a system call + netif->hwaddr[0] = 0x52; + netif->hwaddr[1] = 0x54; + netif->hwaddr[2] = 0x00; + netif->hwaddr[3] = 0x12; + netif->hwaddr[4] = 0x34; + netif->hwaddr[5] = 0x56; +} + +/* + * low_level_output(): + * + * Should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + */ +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + int r = sys_page_alloc(0, (void *)PKTMAP, PTE_U|PTE_W|PTE_P); + if (r < 0) + panic("jif: could not allocate page of memory"); + struct jif_pkt *pkt = (struct jif_pkt *)PKTMAP; + + struct jif *jif; + jif = netif->state; + + char *txbuf = pkt->jp_data; + int txsize = 0; + struct pbuf *q; + for (q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + + if (txsize + q->len > 2000) + panic("oversized packet, fragment %d txsize %d\n", q->len, txsize); + memcpy(&txbuf[txsize], q->payload, q->len); + txsize += q->len; + } + + pkt->jp_len = txsize; + + ipc_send(jif->envid, NSREQ_OUTPUT, (void *)pkt, PTE_P|PTE_W|PTE_U); + sys_page_unmap(0, (void *)pkt); + + return ERR_OK; +} + +/* + * low_level_input(): + * + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + */ +static struct pbuf * +low_level_input(void *va) +{ + struct jif_pkt *pkt = (struct jif_pkt *)va; + s16_t len = pkt->jp_len; + + struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + if (p == 0) + return 0; + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + void *rxbuf = (void *) pkt->jp_data; + int copied = 0; + struct pbuf *q; + for (q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. */ + int bytes = q->len; + if (bytes > (len - copied)) + bytes = len - copied; + memcpy(q->payload, rxbuf + copied, bytes); + copied += bytes; + } + + return p; +} +/* + * jif_output(): + * + * This function is called by the TCP/IP stack when an IP packet + * should be sent. It calls the function called low_level_output() to + * do the actual transmission of the packet. + * + */ + +static err_t +jif_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) +{ + /* resolve hardware address, then send (or queue) packet */ + return etharp_output(netif, p, ipaddr); +} + +/* + * jif_input(): + * + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. + * + */ + +void +jif_input(struct netif *netif, void *va) +{ + struct jif *jif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + jif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(va); + + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + case ETHTYPE_IP: + /* update ARP table */ + etharp_ip_input(netif, p); + /* skip Ethernet header */ + pbuf_header(p, -(int)sizeof(struct eth_hdr)); + /* pass to network layer */ + netif->input(p, netif); + break; + + case ETHTYPE_ARP: + /* pass p to ARP module */ + etharp_arp_input(netif, jif->ethaddr, p); + break; + + default: + pbuf_free(p); + } +} + +/* + * jif_init(): + * + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + */ + +err_t +jif_init(struct netif *netif) +{ + struct jif *jif; + envid_t *output_envid; + + jif = mem_malloc(sizeof(struct jif)); + + if (jif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("jif_init: out of memory\n")); + return ERR_MEM; + } + + output_envid = (envid_t *)netif->state; + + netif->state = jif; + netif->output = jif_output; + netif->linkoutput = low_level_output; + memcpy(&netif->name[0], "en", 2); + + jif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + jif->envid = *output_envid; + + low_level_init(netif); + + etharp_init(); + + // qemu user-net is dumb; if the host OS does not send and ARP request + // first, the qemu will send packets destined for the host using the mac + // addr 00:00:00:00:00; do a arp request for the user-net NAT at 10.0.2.2 + uint32_t ipaddr = inet_addr("10.0.2.2"); + etharp_query(netif, (struct ip_addr *) &ipaddr, 0); + + return ERR_OK; +} diff --git a/lab/net/lwip/jos/jif/jif.h b/lab/net/lwip/jos/jif/jif.h new file mode 100644 index 0000000..0ffe020 --- /dev/null +++ b/lab/net/lwip/jos/jif/jif.h @@ -0,0 +1,4 @@ +#include + +void jif_input(struct netif *netif, void *va); +err_t jif_init(struct netif *netif); diff --git a/lab/net/lwip/jos/lwipopts.h b/lab/net/lwip/jos/lwipopts.h new file mode 100644 index 0000000..58e805f --- /dev/null +++ b/lab/net/lwip/jos/lwipopts.h @@ -0,0 +1,63 @@ +#ifndef JOS_LWIP_LWIPOPTS_H +#define JOS_LWIP_LWIPOPTS_H + +// Huge hack to include memcpy. Since this is the only file that is +// consistently included in all of lwip, a definition of memcpy can be added +// here to make it lwip visible. I am hiding lwip because JOS seems to want to +// do so. There is a declaration of memcpy in JOS but not a definition. +#include +void *memcpy(void *dst, const void *src, size_t n); + +//#define NO_SYS 1 + +#define LWIP_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define LWIP_DHCP 1 +#define LWIP_COMPAT_SOCKETS 0 +//#define SYS_LIGHTWEIGHT_PROT 1 +#define LWIP_PROVIDE_ERRNO 1 + +// Various tuning knobs, see: +// http://lists.gnu.org/archive/html/lwip-users/2006-11/msg00007.html + +#define MEM_ALIGNMENT 4 + +#define MEMP_NUM_PBUF 64 +#define MEMP_NUM_UDP_PCB 8 +#define MEMP_NUM_TCP_PCB 32 +#define MEMP_NUM_TCP_PCB_LISTEN 16 +#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN// at least as big as TCP_SND_QUEUELEN +#define MEMP_NUM_NETBUF 128 +#define MEMP_NUM_NETCONN 32 +#define MEMP_NUM_SYS_TIMEOUT 6 + +#define PER_TCP_PCB_BUFFER (16 * 4096) +#define MEM_SIZE (PER_TCP_PCB_BUFFER*MEMP_NUM_TCP_SEG + 4096*MEMP_NUM_TCP_SEG) + +#define PBUF_POOL_SIZE 512 +#define PBUF_POOL_BUFSIZE 2000 + +#define TCP_MSS 1460 +#define TCP_WND 24000 +#define TCP_SND_BUF (16 * TCP_MSS) +// lwip prints a warning if TCP_SND_QUEUELEN < (2 * TCP_SND_BUF/TCP_MSS), +// but 16 is faster.. +#define TCP_SND_QUEUELEN (2 * TCP_SND_BUF/TCP_MSS) +//#define TCP_SND_QUEUELEN 16 + +// Print error messages when we run out of memory +#define LWIP_DEBUG 1 +//#define TCP_DEBUG LWIP_DBG_ON +//#define MEMP_DEBUG LWIP_DBG_ON +//#define SOCKETS_DEBUG LWIP_DBG_ON +//#define DBG_TYPES_ON LWIP_DBG_ON +//#define PBUF_DEBUG LWIP_DBG_ON +//#define API_LIB_DEBUG LWIP_DBG_ON + +#define DBG_MIN_LEVEL DBG_LEVEL_SERIOUS +#define LWIP_DBG_MIN_LEVEL 0 +#define MEMP_SANITY_CHECK 0 + +#define ERRNO + +#endif diff --git a/lab/net/lwip/netif/FILES b/lab/net/lwip/netif/FILES new file mode 100644 index 0000000..1c4f592 --- /dev/null +++ b/lab/net/lwip/netif/FILES @@ -0,0 +1,25 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +etharp.c + Implements the ARP (Address Resolution Protocol) over + Ethernet. The code in this file should be used together with + Ethernet device drivers. Note that this module has been + largely made Ethernet independent so you should be able to + adapt this for other link layers (such as Firewire). + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +loopif.c + A "loopback" network interface driver. It requires configuration + through the define LWIP_LOOPIF_MULTITHREADING (see opt.h). + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack diff --git a/lab/net/lwip/netif/etharp.c b/lab/net/lwip/netif/etharp.c new file mode 100644 index 0000000..f5a6473 --- /dev/null +++ b/lab/net/lwip/netif/etharp.c @@ -0,0 +1,1186 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_query(our_netif, its_ip_addr, NULL) upon + * address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/inet.h" +#include "lwip/ip.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 5000, this is + * (240 * 5) seconds = 20 minutes. + */ +#define ARP_MAXAGE 240 +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 5000, this is + * (2 * 5) seconds = 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 2 + +#define HWTYPE_ETHERNET 1 + +#define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8) +#define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) + +#define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8)) +#define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) + +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** + * Pointer to queue of pending outgoing packets on this ARP entry. + */ + struct etharp_q_entry *q; +#endif + struct ip_addr ipaddr; + struct eth_addr ethaddr; + enum etharp_state state; + u8_t ctime; + struct netif *netif; +}; + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif + +/** + * Try hard to create a new entry - we want the IP address to appear in + * the cache (even if this means removing an active entry or so). */ +#define ETHARP_TRY_HARD 1 +#define ETHARP_FIND_ONLY 2 + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif); +#else /* LWIP_NETIF_HWADDRHINT */ +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags); +#endif /* LWIP_NETIF_HWADDRHINT */ + +static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags); + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it in your lwipopts.h" +#endif + + +#if ARP_QUEUEING +/** + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free + */ +static void +free_etharp_q(struct etharp_q_entry *q) +{ + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); + } +} +#endif + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL microseconds (5 seconds), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + arp_table[i].ctime++; + if (((arp_table[i].state == ETHARP_STATE_STABLE) && + (arp_table[i].ctime >= ARP_MAXAGE)) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); +#if ARP_QUEUEING + /* and empty packet queue */ + if (arp_table[i].q != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; + } +#endif + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; + } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags + * - ETHARP_TRY_HARD: Try hard to create a entry by allowing recycling of + * active (stable or pending) entries. + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t +#if LWIP_NETIF_HWADDRHINT +find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif) +#else /* LWIP_NETIF_HWADDRHINT */ +find_entry(struct ip_addr *ipaddr, u8_t flags) +#endif /* LWIP_NETIF_HWADDRHINT */ +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0, age_pending = 0, age_stable = 0; +#if ARP_QUEUEING + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; +#endif + + /* First, test if the last call to this function asked for the + * same address. If so, we're really fast! */ + if (ipaddr) { + /* ipaddr to search for was given */ +#if LWIP_NETIF_HWADDRHINT + if ((netif != NULL) && (netif->addr_hint != NULL)) { + /* per-pcb cached entry was given */ + u8_t per_pcb_cache = *(netif->addr_hint); + if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == ETHARP_STATE_STABLE) { + /* the per-pcb-cached entry is stable */ + if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) { + /* per-pcb cached entry was the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return per_pcb_cache; + } + } + } +#else /* #if LWIP_NETIF_HWADDRHINT */ + if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) { + /* the cached entry is stable */ + if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) { + /* cached entry was the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_cached_entry; + } + } +#endif /* #if LWIP_NETIF_HWADDRHINT */ + } + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } + /* pending entry? */ + else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + return i; +#if ARP_QUEUEING + /* pending with queued packets? */ + } else if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } +#endif + /* pending without queued packets? */ + } else { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + } + /* stable entry? */ + else if (arp_table[i].state == ETHARP_STATE_STABLE) { + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + return i; + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + } else if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + /* { we have no match } => try to create a new entry */ + + /* no empty entry found and not allowed to recycle? */ + if (((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) + /* or don't create new entry, only search? */ + || ((flags & ETHARP_FIND_ONLY) != 0)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets + * + * { ETHARP_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } + /* 2) found recyclable stable entry? */ + else if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); +#if ARP_QUEUEING + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); +#endif + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); +#if ARP_QUEUEING + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; +#endif + /* no empty or recyclable entries found */ + } else { + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + + if (arp_table[i].state != ETHARP_STATE_EMPTY) + { + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + } + /* recycle entry (no-op for an already empty entry) */ + arp_table[i].state = ETHARP_STATE_EMPTY; + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_set(&arp_table[i].ipaddr, ipaddr); + } + arp_table[i].ctime = 0; +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + return (err_t)i; +} + +/** + * Send an IP packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = p->payload; + u8_t k; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + k = ETHARP_HWADDR_LEN; + while(k > 0) { + k--; + ethhdr->dest.addr[k] = dst->addr[k]; + ethhdr->src.addr[k] = src->addr[k]; + } + ethhdr->type = htons(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags Defines behaviour: + * - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified, + * only existing ARP entries will be updated. + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t +update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + u8_t k; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 3, ("update_arp_entry()\n")); + LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + /* non-unicast address? */ + if (ip_addr_isany(ipaddr) || + ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, flags, netif); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, flags); +#endif /* LWIP_NETIF_HWADDRHINT */ + /* bail out if no entry could be found */ + if (i < 0) + return (err_t)i; + + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + /* record network interface */ + arp_table[i].netif = netif; + + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + k = ETHARP_HWADDR_LEN; + while (k > 0) { + k--; + arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; + } + /* reset time stamp */ + arp_table[i].ctime = 0; +#if ARP_QUEUEING + /* this is where we will send out queued packets! */ + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); + /* send the queued IP packet */ + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); + /* free the queued IP packet */ + pbuf_free(p); + } +#endif + return ERR_OK; +} + +/** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, + struct eth_addr **eth_ret, struct ip_addr **ip_ret) +{ + s8_t i; + + LWIP_UNUSED_ARG(netif); + +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, ETHARP_FIND_ONLY, NULL); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, ETHARP_FIND_ONLY); +#endif /* LWIP_NETIF_HWADDRHINT */ + if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +/** + * Updates the ARP table using the given IP packet. + * + * Uses the incoming IP packet's source address to update the + * ARP cache for the local network. The function does not alter + * or free the packet. This function must be called before the + * packet p is passed to the IP layer. + * + * @param netif The lwIP network interface on which the IP packet pbuf arrived. + * @param p The IP packet that arrived on netif. + * + * @return NULL + * + * @see pbuf_free() + */ +void +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct ethip_hdr *hdr; + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + /* Only insert an entry if the source IP address of the + incoming IP packet comes from a host on the local network. */ + hdr = p->payload; + /* source is not on the local network? */ + if (!ip_addr_netcmp(&(hdr->ip.src), &(netif->ip_addr), &(netif->netmask))) { + /* do nothing */ + return; + } + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + /* update ARP table */ + /* @todo We could use ETHARP_TRY_HARD if we think we are going to talk + * back soon (for example, if the destination IP address is ours. */ + update_arp_entry(netif, &(hdr->ip.src), &(hdr->eth.src), 0); +} + + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * @param ethaddr Ethernet address of netif. + * @param p The ARP packet that arrived on netif. Is freed by this function. + * + * @return NULL + * + * @see pbuf_free() + */ +void +etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) +{ + struct etharp_hdr *hdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + struct ip_addr sipaddr, dipaddr; + u8_t i; + u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* drop short ARP packets: we have to check for p->len instead of p->tot_len here + since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */ + if (p->len < sizeof(struct etharp_hdr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, (s16_t)sizeof(struct etharp_hdr))); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + + hdr = p->payload; + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != htons(HWTYPE_ETHERNET)) || + (hdr->_hwlen_protolen != htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr))) || + (hdr->proto != htons(ETHTYPE_IP)) || + (hdr->ethhdr.type != htons(ETHTYPE_ARP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 1, + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, ARPH_HWLEN(hdr), hdr->proto, ARPH_PROTOLEN(hdr), hdr->ethhdr.type)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); + SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); + + /* this interface is not configured? */ + if (netif->ip_addr.addr == 0) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? */ + if (for_us) { + /* add IP address in ARP cache; assume requester wants to talk to us. + * can result in directly sending the queued packets for this host. */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD); + /* ARP message not directed to us? */ + } else { + /* update the source IP address in the cache, if present */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0); + } + + /* now act on the message itself */ + switch (htons(hdr->opcode)) { + /* ARP request? */ + case ARP_REQUEST: + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possiby send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* Re-use pbuf to send ARP reply. + Since we are re-using an existing pbuf, we can't call etharp_raw since + that would allocate a new pbuf. */ + hdr->opcode = htons(ARP_REPLY); + + hdr->dipaddr = hdr->sipaddr; + hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + i = ETHARP_HWADDR_LEN; +#if LWIP_AUTOIP + /* If we are using Link-Local, ARP packets must be broadcast on the + * link layer. (See RFC3927 Section 2.5) */ + ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + while(i > 0) { + i--; + hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; +#if LWIP_AUTOIP + hdr->ethhdr.dest.addr[i] = ethdst_hwaddr[i]; +#else /* LWIP_AUTOIP */ + hdr->ethhdr.dest.addr[i] = hdr->shwaddr.addr[i]; +#endif /* LWIP_AUTOIP */ + hdr->shwaddr.addr[i] = ethaddr->addr[i]; + hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; + } + + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ + + /* return ARP reply */ + netif->linkoutput(netif, p); + /* we are not configured? */ + } else if (netif->ip_addr.addr == 0) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case ARP_REPLY: + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? */ + dhcp_arp_reply(netif, &sipaddr); +#endif + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IP packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ipaddr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr) +{ + struct eth_addr *dest, mcastaddr; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 2, ("etharp_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; + } + + /* assume unresolved Ethernet address */ + dest = NULL; + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = 0x01; + mcastaddr.addr[1] = 0x00; + mcastaddr.addr[2] = 0x5e; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + /* outside local network? */ + if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) { + /* interface has default gateway? */ + if (netif->gw.addr != 0) { + /* send to hardware address of default gateway IP address */ + ipaddr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + /* queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, ipaddr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + /* send packet directly on the link */ + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @note q must only be ONE packet, not a packet queue! + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t +etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + s8_t i; /* ARP entry index */ + + /* non-unicast address? */ + if (ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, ETHARP_TRY_HARD, netif); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, ETHARP_TRY_HARD); +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* could not find or create entry? */ + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + arp_table[i].state = ETHARP_STATE_PENDING; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state == ETHARP_STATE_STABLE))); + + /* do we have a pending entry? or an implicit query request? */ + if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } + } + + /* packet given? */ + if (q != NULL) { + /* stable entry? */ + if (arp_table[i].state == ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + /* send the packet */ + result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { +#if ARP_QUEUEING /* queue the given q packet */ + struct pbuf *p; + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + new_entry->next = 0; + new_entry->p = p; + if(arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + } else { + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } +#else /* ARP_QUEUEING == 0 */ + /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */ + /* { result == ERR_MEM } through initialization */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q)); +#endif + } + } + return result; +} + +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +#if !LWIP_AUTOIP +static +#endif /* LWIP_AUTOIP */ +err_t +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const struct ip_addr *ipsrc_addr, + const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + u8_t k; /* ARP entry index */ + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 2, ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= sizeof(struct etharp_hdr))); + + hdr = p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = htons(opcode); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + k = ETHARP_HWADDR_LEN; +#if LWIP_AUTOIP + /* If we are using Link-Local, ARP packets must be broadcast on the + * link layer. (See RFC3927 Section 2.5) */ + ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write MAC-Addresses (combined loop for both headers) */ + while(k > 0) { + k--; + /* Write the ARP MAC-Addresses */ + hdr->shwaddr.addr[k] = hwsrc_addr->addr[k]; + hdr->dhwaddr.addr[k] = hwdst_addr->addr[k]; + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + hdr->ethhdr.dest.addr[k] = ethdst_hwaddr[k]; +#else /* LWIP_AUTOIP */ + hdr->ethhdr.dest.addr[k] = ethdst_addr->addr[k]; +#endif /* LWIP_AUTOIP */ + hdr->ethhdr.src.addr[k] = ethsrc_addr->addr[k]; + } + hdr->sipaddr = *(struct ip_addr2 *)ipsrc_addr; + hdr->dipaddr = *(struct ip_addr2 *)ipdst_addr; + + hdr->hwtype = htons(HWTYPE_ETHERNET); + hdr->proto = htons(ETHTYPE_IP); + /* set hwlen and protolen together */ + hdr->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr)); + + hdr->ethhdr.type = htons(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + + return result; +} + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, struct ip_addr *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, + ipaddr, ARP_REQUEST); +} + +/** + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access. + * + * @param p the recevied packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%02x:%02x:%02x:%02x:%02x:%02x, src:%02x:%02x:%02x:%02x:%02x:%02x, type:%2hx\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + (unsigned)htons(ethhdr->type))); + + switch (htons(ethhdr->type)) { + /* IP packet? */ + case ETHTYPE_IP: +#if ETHARP_TRUST_IP_MAC + /* update ARP table */ + etharp_ip_input(netif, p); +#endif /* ETHARP_TRUST_IP_MAC */ + /* skip Ethernet header */ + if(pbuf_header(p, -(s16_t)sizeof(struct eth_hdr))) { + LWIP_ASSERT("Can't move over header in packet", 0); + pbuf_free(p); + p = NULL; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case ETHTYPE_ARP: + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; + +#if PPPOE_SUPPORT + case ETHTYPE_PPPOEDISC: /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case ETHTYPE_PPPOE: /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + + default: + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + p = NULL; + break; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; +} +#endif /* LWIP_ARP */ diff --git a/lab/net/lwip/netif/ethernetif.c b/lab/net/lwip/netif/ethernetif.c new file mode 100644 index 0000000..8e5cefc --- /dev/null +++ b/lab/net/lwip/netif/ethernetif.c @@ -0,0 +1,313 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * This file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" + +#if 0 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include +#include +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + + initiate transfer(); + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * +low_level_input(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + drop packet(); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + ethernetif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_ARP: +#if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif)!=ERR_OK) + { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, ???); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/lab/net/lwip/netif/loopif.c b/lab/net/lwip/netif/loopif.c new file mode 100644 index 0000000..1e1f28c --- /dev/null +++ b/lab/net/lwip/netif/loopif.c @@ -0,0 +1,66 @@ +/** + * @file + * Loop Interface + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#include "lwip/opt.h" + +#if LWIP_HAVE_LOOPIF + +#include "netif/loopif.h" +#include "lwip/snmp.h" + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ +err_t +loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; + netif->output = netif_loop_output; + return ERR_OK; +} + +#endif /* LWIP_HAVE_LOOPIF */ diff --git a/lab/net/lwip/netif/ppp/auth.c b/lab/net/lwip/netif/ppp/auth.c new file mode 100644 index 0000000..4c0ee6a --- /dev/null +++ b/lab/net/lwip/netif/ppp/auth.c @@ -0,0 +1,988 @@ +/***************************************************************************** +* auth.c - Network Authentication and Phase Control program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Ported from public pppd code. +*****************************************************************************/ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "lcp.h" +#include "pap.h" +#include "chap.h" +#include "auth.h" +#include "ipcp.h" + +#if CBCP_SUPPORT +#include "cbcp.h" +#endif /* CBCP_SUPPORT */ + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +extern char *crypt (const char *, const char *); + +/* Prototypes for procedures local to this file. */ + +static void network_phase (int); +static void check_idle (void *); +static void connect_time_expired (void *); +#if 0 +static int login (char *, char *, char **, int *); +#endif +static void logout (void); +static int null_login (int); +static int get_pap_passwd (int, char *, char *); +static int have_pap_secret (void); +static int have_chap_secret (char *, char *, u32_t); +static int ip_addr_check (u32_t, struct wordlist *); +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +static void set_allowed_addrs(int unit, struct wordlist *addrs); +static void free_wordlist (struct wordlist *); +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ +#if CBCP_SUPPORT +static void callback_phase (int); +#endif /* CBCP_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +#if PAP_SUPPORT || CHAP_SUPPORT +/* The name by which the peer authenticated itself to us. */ +static char peer_authname[MAXNAMELEN]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called login() */ +static int logged_in; + +/* Set if we have run the /etc/ppp/auth-up script. */ +static int did_authup; + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +/* Number of network protocols which we have opened. */ +static int num_np_open; + +/* Number of network protocols which have come up. */ +static int num_np_up; + +#if PAP_SUPPORT || CHAP_SUPPORT +/* Set if we got the contents of passwd[] from the pap-secrets file. */ +static int passwd_from_file; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(int unit) +{ + LWIP_UNUSED_ARG(unit); + + AUTHDEBUG((LOG_INFO, "link_required: %d\n", unit)); +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(int unit) +{ + AUTHDEBUG((LOG_INFO, "link_terminated: %d\n", unit)); + if (lcp_phase[unit] == PHASE_DEAD) { + return; + } + if (logged_in) { + logout(); + } + lcp_phase[unit] = PHASE_DEAD; + AUTHDEBUG((LOG_NOTICE, "Connection terminated.\n")); + pppLinkTerminated(unit); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(int unit) +{ + int i; + struct protent *protp; + + AUTHDEBUG((LOG_INFO, "link_down: %d\n", unit)); + if (did_authup) { + /* XXX Do link down processing. */ + did_authup = 0; + } + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) { + continue; + } + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) { + (*protp->lowerdown)(unit); + } + if (protp->protocol < 0xC000 && protp->close != NULL) { + (*protp->close)(unit, "LCP down"); + } + } + num_np_open = 0; + num_np_up = 0; + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + pppLinkDown(unit); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(int unit) +{ + int auth; + int i; + struct protent *protp; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; +#if PAP_SUPPORT || CHAP_SUPPORT + lcp_options *ho = &lcp_hisoptions[unit]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + AUTHDEBUG((LOG_INFO, "link_established: %d\n", unit)); + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) { + (*protp->lowerup)(unit); + } + } + if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + AUTHDEBUG((LOG_WARNING, "peer refused to authenticate\n")); + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + lcp_phase[unit] = PHASE_AUTHENTICATE; + auth = 0; +#if CHAP_SUPPORT + if (go->neg_chap) { + ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + if (ppp_settings.passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) { + AUTHDEBUG((LOG_ERR, "No secret found for PAP login\n")); + } + } + upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); + auth |= PAP_WITHPEER; + } +#endif /* PAP_SUPPORT */ + auth_pending[unit] = auth; + + if (!auth) { + network_phase(unit); + } +} + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(int unit, u16_t protocol) +{ + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG((LOG_INFO, "auth_peer_fail: %d proto=%X\n", unit, protocol)); + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + + +#if PAP_SUPPORT || CHAP_SUPPORT +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(int unit, u16_t protocol, char *name, int namelen) +{ + int pbit; + + AUTHDEBUG((LOG_INFO, "auth_peer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_PEER; + break; + case PPP_PAP: + pbit = PAP_PEER; + break; + default: + AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol)); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > sizeof(peer_authname) - 1) { + namelen = sizeof(peer_authname) - 1; + } + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(int unit, u16_t protocol) +{ + int errCode = PPPERR_AUTHFAIL; + + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG((LOG_INFO, "auth_withpeer_fail: %d proto=%X\n", unit, protocol)); + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + /* + * XXX Warning: the unit number indicates the interface which is + * not necessarily the PPP connection. It works here as long + * as we are only supporting PPP interfaces. + */ + pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode); + + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(int unit, u16_t protocol) +{ + int pbit; + + AUTHDEBUG((LOG_INFO, "auth_withpeer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + pbit = PAP_WITHPEER; + break; + default: + AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol)); + pbit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/* + * np_up - a network protocol has come up. + */ +void +np_up(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_up: %d proto=%X\n", unit, proto)); + if (num_np_up == 0) { + AUTHDEBUG((LOG_INFO, "np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); + /* + * At this point we consider that the link has come up successfully. + */ + if (ppp_settings.idle_time_limit > 0) { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); + } + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (ppp_settings.maxconnect > 0) { + TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + } + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_down: %d proto=%X\n", unit, proto)); + if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_finished: %d proto=%X\n", unit, proto)); + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(int unit) +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + AUTHDEBUG((LOG_INFO, "auth_reset: %d\n", unit)); + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); + ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; + + if (go->neg_upap && !have_pap_secret()) { + go->neg_upap = 0; + } + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) { + go->neg_chap = 0; + } + } +} + +#if PAP_SUPPORT +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen) +{ +#if 1 + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(auser); + LWIP_UNUSED_ARG(userlen); + LWIP_UNUSED_ARG(apasswd); + LWIP_UNUSED_ARG(passwdlen); + LWIP_UNUSED_ARG(msglen); + *msg = (char *) 0; + return UPAP_AUTHACK; /* XXX Assume all entries OK. */ +#else + int ret = 0; + struct wordlist *addrs = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static u_short attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + *msg = (char *) 0; + + /* XXX Validate user name and password. */ + ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ + + if (ret == UPAP_AUTHNAK) { + if (*msg == (char *) 0) { + *msg = "Login incorrect"; + } + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + AUTHDEBUG((LOG_WARNING, "%d LOGIN FAILURES BY %s\n", attempts, user)); + /*ppp_panic("Excess Bad Logins");*/ + } + if (attempts > 3) { + sys_msleep((attempts - 3) * 5); + } + if (addrs != NULL) { + free_wordlist(addrs); + } + } else { + attempts = 0; /* Reset count */ + if (*msg == (char *) 0) { + *msg = "Login ok"; + } + *msglen = strlen(*msg); + set_allowed_addrs(unit, addrs); + } + + BZERO(passwd, sizeof(passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +#endif +} +#endif /* PAP_SUPPORT */ + + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(int unit, u32_t addr) +{ + return ip_addr_check(addr, addresses[unit]); +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(u32_t addr) +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + + +#if CHAP_SUPPORT +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int get_secret( int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs) +{ +#if 1 + int len; + struct wordlist *addrs; + + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(save_addrs); + + addrs = NULL; + + if(!client || !client[0] || strcmp(client, ppp_settings.user)) { + return 0; + } + + len = strlen(ppp_settings.passwd); + if (len > MAXSECRETLEN) { + AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(ppp_settings.passwd, secret, len); + *secret_len = len; + + return 1; +#else + int ret = 0, len; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + addrs = NULL; + secbuf[0] = 0; + + /* XXX Find secret. */ + if (ret < 0) { + return 0; + } + + if (save_addrs) { + set_allowed_addrs(unit, addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(secbuf, secret, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif +} +#endif /* CHAP_SUPPORT */ + + +#if 0 /* UNUSED */ +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options(void) +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) { + strcpy(ppp_settings.our_name, ppp_settings.hostname); + } + + if (ppp_settings.user[0] == 0) { + strcpy(ppp_settings.user, ppp_settings.our_name); + } + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && have_pap_secret(); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); + } + + if (ppp_settings.auth_required && !can_auth) { + ppp_panic("No auth secret"); + } +} +#endif + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * Proceed to the network phase. + */ +static void +network_phase(int unit) +{ + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + /* XXX Do setup for peer authentication. */ + did_authup = 1; + } + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + lcp_phase[unit] = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif /* CBCP_SUPPORT */ + + lcp_phase[unit] = PHASE_NETWORK; + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) { + ++num_np_open; + } + } + } + + if (num_np_open == 0) { + /* nothing to do */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(void *arg) +{ + struct ppp_idle idle; + u_short itime; + + LWIP_UNUSED_ARG(arg); + if (!get_idle_time(0, &idle)) { + return; + } + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= ppp_settings.idle_time_limit) { + /* link is idle: shut it down. */ + AUTHDEBUG((LOG_INFO, "Terminating connection due to lack of activity.\n")); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + AUTHDEBUG((LOG_INFO, "Connect time expired\n")); + lcp_close(0, "Connect time expired"); /* Close connection */ +} + +#if 0 +/* + * login - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +static int +login(char *user, char *passwd, char **msg, int *msglen) +{ + /* XXX Fail until we decide that we want to support logins. */ + return (UPAP_AUTHNAK); +} +#endif + +/* + * logout - Logout the user. + */ +static void +logout(void) +{ + logged_in = 0; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* XXX Fail until we decide that we want to support logins. */ + return 0; +} + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ +static int +get_pap_passwd(int unit, char *user, char *passwd) +{ + LWIP_UNUSED_ARG(unit); +/* normally we would reject PAP if no password is provided, + but this causes problems with some providers (like CHT in Taiwan) + who incorrectly request PAP and expect a bogus/empty password, so + always provide a default user/passwd of "none"/"none" +*/ + if(user) { + strcpy(user, "none"); + } + if(passwd) { + strcpy(passwd, "none"); + } + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(void) +{ + /* XXX Fail until we set up our passwords. */ + return 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(char *client, char *server, u32_t remote) +{ + LWIP_UNUSED_ARG(client); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(remote); + /* XXX Fail until we set up our passwords. */ + return 0; +} + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * set_allowed_addrs() - set the list of allowed addresses. + */ +static void +set_allowed_addrs(int unit, struct wordlist *addrs) +{ + if (addresses[unit] != NULL) { + free_wordlist(addresses[unit]); + } + addresses[unit] = addrs; + +#if 0 + /* + * If there's only one authorized address we might as well + * ask our peer for that one right away + */ + if (addrs != NULL && addrs->next == NULL) { + char *p = addrs->word; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t a; + struct hostent *hp; + + if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) { + hp = gethostbyname(p); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + a = inet_addr(p); + } + if (a != (u32_t) -1) { + wo->hisaddr = a; + } + } + } +#endif +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +static int +ip_addr_check(u32_t addr, struct wordlist *addrs) +{ + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) { + return 0; + } + + if (addrs == NULL) { + return !ppp_settings.auth_required; /* no addresses authorized */ + } + + /* XXX All other addresses allowed. */ + return 1; +} + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(struct wordlist *wp) +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/auth.h b/lab/net/lwip/netif/ppp/auth.h new file mode 100644 index 0000000..86ff049 --- /dev/null +++ b/lab/net/lwip/netif/ppp/auth.h @@ -0,0 +1,111 @@ +/***************************************************************************** +* auth.h - PPP Authentication and phase control header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD pppd.h. +*****************************************************************************/ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef AUTH_H +#define AUTH_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* we are starting to use the link */ +void link_required (int); + +/* we are finished with the link */ +void link_terminated (int); + +/* the LCP layer has left the Opened state */ +void link_down (int); + +/* the link is up; authenticate now */ +void link_established (int); + +/* a network protocol has come up */ +void np_up (int, u16_t); + +/* a network protocol has gone down */ +void np_down (int, u16_t); + +/* a network protocol no longer needs link */ +void np_finished (int, u16_t); + +/* peer failed to authenticate itself */ +void auth_peer_fail (int, u16_t); + +/* peer successfully authenticated itself */ +void auth_peer_success (int, u16_t, char *, int); + +/* we failed to authenticate ourselves */ +void auth_withpeer_fail (int, u16_t); + +/* we successfully authenticated ourselves */ +void auth_withpeer_success (int, u16_t); + +/* check authentication options supplied */ +void auth_check_options (void); + +/* check what secrets we have */ +void auth_reset (int); + +/* Check peer-supplied username/password */ +int check_passwd (int, char *, int, char *, int, char **, int *); + +/* get "secret" for chap */ +int get_secret (int, char *, char *, char *, int *, int); + +/* check if IP address is authorized */ +int auth_ip_addr (int, u32_t); + +/* check if IP address is unreasonable */ +int bad_ip_adrs (u32_t); + +#endif /* AUTH_H */ diff --git a/lab/net/lwip/netif/ppp/chap.c b/lab/net/lwip/netif/ppp/chap.c new file mode 100644 index 0000000..6d9c3c3 --- /dev/null +++ b/lab/net/lwip/netif/ppp/chap.c @@ -0,0 +1,902 @@ +/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/ +/***************************************************************************** +* chap.c - Network Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap.c. +*****************************************************************************/ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "magic.h" +#include "randm.h" +#include "auth.h" +#include "md5.h" +#include "chap.h" +#include "chpms.h" + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Protocol entry points. + */ +static void ChapInit (int); +static void ChapLowerUp (int); +static void ChapLowerDown (int); +static void ChapInput (int, u_char *, int); +static void ChapProtocolReject (int); +#if 0 +static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *); +#endif + +static void ChapChallengeTimeout (void *); +static void ChapResponseTimeout (void *); +static void ChapReceiveChallenge (chap_state *, u_char *, int, int); +static void ChapRechallenge (void *); +static void ChapReceiveResponse (chap_state *, u_char *, int, int); +static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapSendStatus (chap_state *, int); +static void ChapSendChallenge (chap_state *); +static void ChapSendResponse (chap_state *); +static void ChapGenChallenge (chap_state *); + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, +#if 0 + ChapPrintPkt, + NULL, +#endif + 1, + "CHAP", +#if 0 + NULL, + NULL, + NULL +#endif +}; + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(int unit, char *our_name, int digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(int unit, char *our_name, int digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(int unit) +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) { + return; + } + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + CHAPDEBUG((LOG_ERR, "Peer failed to respond to CHAP challenge\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) { + return; + } + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) { + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) { + cstate->clientstate = CHAPCS_CLOSED; + } else if (cstate->clientstate == CHAPCS_PENDING) { + cstate->clientstate = CHAPCS_LISTEN; + } + + if (cstate->serverstate == CHAPSS_INITIAL) { + cstate->serverstate = CHAPSS_CLOSED; + } else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(int unit) +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) { + UNTIMEOUT(ChapChallengeTimeout, cstate); + } else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) { + UNTIMEOUT(ChapRechallenge, cstate); + } + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) { + auth_peer_fail(unit, PPP_CHAP); + } + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) { + auth_withpeer_fail(unit, PPP_CHAP); + } + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(int unit, u_char *inpacket, int packet_len) +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.\n")); + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.\n")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + CHAPDEBUG((LOG_WARNING, "Unknown CHAP code (%d) received.\n", code)); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(chap_state *cstate, u_char *inp, int id, int len) +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.\n", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d\n", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'\n", rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name\n", rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating us to %s\n", rhostname)); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#ifdef CHAPMS + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.\n", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d\n", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) { + return; /* doesn't match ID of last challenge */ + } + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.\n")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.\n")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s\n", rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, secret, &secret_len, 1)) { + /* CHAPDEBUG((LOG_WARNING, TL_CHAP, "No CHAP secret found for authenticating %s\n", rhostname)); */ + CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating %s\n", + rhostname)); + } else { + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) { + break; /* it's not even the right length */ + } + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) { + code = CHAP_SUCCESS; /* they are the same! */ + } + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) { + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + } + } else { + CHAPDEBUG((LOG_ERR, "CHAP peer authentication failed\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.\n", id)); + + if (cstate->clientstate == CHAPCS_OPEN) { + /* presumably an answer to a duplicate response */ + return; + } + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.\n", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + CHAPDEBUG((LOG_ERR, "CHAP authentication failed\n")); + auth_withpeer_fail(cstate->unit, PPP_CHAP); +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(chap_state *cstate) +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.\n", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(chap_state *cstate, int code) +{ + u_char *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) { + strcpy(msg, "Welcome!"); + } else { + strcpy(msg, "I don't like you. Go 'way."); + } + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.\n", code, cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(chap_state *cstate) +{ + int chal_len; + u_char *ptr = cstate->challenge; + int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) + ((((magic() >> 16) * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) { + *ptr++ = (char) (magic() & 0xff); + } +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(chap_state *cstate) +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +#if 0 +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static int +ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) { + return 0; + } + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) { + return 0; + } + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) { + printer(arg, " %s", ChapCodenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) { + break; + } + clen = p[0]; + if (len < clen + 1) { + break; + } + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = %.*Z", nlen, p); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " %.*Z", len, p); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} +#endif + +#endif /* CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/chap.h b/lab/net/lwip/netif/ppp/chap.h new file mode 100644 index 0000000..83dafd7 --- /dev/null +++ b/lab/net/lwip/netif/ppp/chap.h @@ -0,0 +1,166 @@ +/***************************************************************************** +* chap.h - Network Challenge Handshake Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-03 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chap.h,v 1.4 2007/12/19 20:47:22 fbernon Exp $ + */ + +#ifndef CHAP_H +#define CHAP_H + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/****************** +*** PUBLIC DATA *** +******************/ +extern chap_state chap[]; + +extern struct protent chap_protent; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +void ChapAuthWithPeer (int, char *, int); +void ChapAuthPeer (int, char *, int); + +#endif /* CHAP_H */ diff --git a/lab/net/lwip/netif/ppp/chpms.c b/lab/net/lwip/netif/ppp/chpms.c new file mode 100644 index 0000000..0c7521f --- /dev/null +++ b/lab/net/lwip/netif/ppp/chpms.c @@ -0,0 +1,396 @@ +/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/ +/***************************************************************************** +* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap_ms.c. +*****************************************************************************/ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#define USE_CRYPT + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "md4.h" +#ifndef USE_CRYPT +#include "des.h" +#endif +#include "chap.h" +#include "chpms.h" + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ + +/* XXX Don't know what to do with these. */ +extern void setkey(const char *); +extern void encrypt(char *, int); + +static void DesEncrypt (u_char *, u_char *, u_char *); +static void MakeKey (u_char *, u_char *); + +#ifdef USE_CRYPT +static void Expand (u_char *, u_char *); +static void Collapse (u_char *, u_char *); +#endif + +static void ChallengeResponse( + u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */ +); +static void ChapMS_NT( + char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response +); +static u_char Get7Bits( + u_char *input, + int startBit +); + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +void +ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len) +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'\n", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +static void +ChallengeResponse( u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */) +{ + char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, 16); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey(crypt_key); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt(des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char +Get7Bits( u_char *input, int startBit) +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void +Expand(u_char *in, u_char *out) +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) { + *out++ = (c >> j) & 01; + } + i += 8; + } +} + +/* The inverse of Expand + */ +static void +Collapse(u_char *in, u_char *out) +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) { + c |= *in << j; + } + *out = c & 0xff; + } +} +#endif + +static void +MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */ + u_char *des_key /* OUT 64 bit DES key with parity bits added */) +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + MDstruct md4Context; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + static int low_byte_first = -1; + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) { + unicodePassword[i * 2] = (u_char)secret[i]; + } + MDbegin(&md4Context); + MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + if (low_byte_first == -1) { + low_byte_first = (htons((unsigned short int)1) != 1); + } + if (low_byte_first == 0) { + MDreverse((u_long *)&md4Context); /* sfb 961105 */ + } + + MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ + + ChallengeResponse(rchallenge, (char *)md4Context.buffer, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[16]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) { + UcasePassword[i] = (u_char)toupper(secret[i]); + } + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +#endif /* MSCHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/chpms.h b/lab/net/lwip/netif/ppp/chpms.h new file mode 100644 index 0000000..df070fb --- /dev/null +++ b/lab/net/lwip/netif/ppp/chpms.h @@ -0,0 +1,64 @@ +/***************************************************************************** +* chpms.h - Network Microsoft Challenge Handshake Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-01-30 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef CHPMS_H +#define CHPMS_H + +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS (chap_state *, char *, int, char *, int); + +#endif /* CHPMS_H */ diff --git a/lab/net/lwip/netif/ppp/fsm.c b/lab/net/lwip/netif/ppp/fsm.c new file mode 100644 index 0000000..c073f1e --- /dev/null +++ b/lab/net/lwip/netif/ppp/fsm.c @@ -0,0 +1,906 @@ +/***************************************************************************** +* fsm.c - Network Control Protocol Finite State Machine program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD fsm.c. +*****************************************************************************/ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "fsm.h" + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +#if PPP_DEBUG + +static const char *ppperr_strerr[] = { + "LS_INITIAL", /* LS_INITIAL 0 */ + "LS_STARTING", /* LS_STARTING 1 */ + "LS_CLOSED", /* LS_CLOSED 2 */ + "LS_STOPPED", /* LS_STOPPED 3 */ + "LS_CLOSING", /* LS_CLOSING 4 */ + "LS_STOPPING", /* LS_STOPPING 5 */ + "LS_REQSENT", /* LS_REQSENT 6 */ + "LS_ACKRCVD", /* LS_ACKRCVD 7 */ + "LS_ACKSENT", /* LS_ACKSENT 8 */ + "LS_OPENED" /* LS_OPENED 9 */ +}; + +#endif /* PPP_DEBUG */ + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +static void fsm_timeout (void *); +static void fsm_rconfreq (fsm *, u_char, u_char *, int); +static void fsm_rconfack (fsm *, int, u_char *, int); +static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); +static void fsm_rtermreq (fsm *, int, u_char *, int); +static void fsm_rtermack (fsm *); +static void fsm_rcoderej (fsm *, u_char *, int); +static void fsm_sconfreq (fsm *, int); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +int peer_mru[NUM_PPP]; + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(fsm *f) +{ + f->state = LS_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = FSM_DEFTIMEOUT; + f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; + f->maxtermtransmits = FSM_DEFMAXTERMREQS; + f->maxnakloops = FSM_DEFMAXNAKLOOPS; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_CLOSED; + break; + + case LS_STARTING: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Up event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG((LOG_INFO, "%s: lowerup state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_CLOSED: + f->state = LS_INITIAL; + break; + + case LS_STOPPED: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSING: + f->state = LS_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + f->state = LS_STARTING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Down event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG((LOG_INFO, "%s: lowerdown state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSED: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + case LS_CLOSING: + f->state = LS_STOPPING; + /* fall through */ + case LS_STOPPED: + case LS_OPENED: + if( f->flags & OPT_RESTART ) { + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } + + FSMDEBUG((LOG_INFO, "%s: open state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the LS_CLOSED state. + */ +void +fsm_close(fsm *f, char *reason) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: strlen(reason)); + switch( f->state ) { + case LS_STARTING: + f->state = LS_INITIAL; + break; + case LS_STOPPED: + f->state = LS_CLOSED; + break; + case LS_STOPPING: + f->state = LS_CLOSING; + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + case LS_OPENED: + if( f->state != LS_OPENED ) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + } else if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_CLOSING; + break; + } + + FSMDEBUG((LOG_INFO, "%s: close reason=%s state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen) +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf[f->unit]; + if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { + datalen = peer_mru[f->unit] - HEADERLEN; + } + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + } + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); + FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d,%d,%d.\n", + PROTO_NAME(f), code, id, outlen)); +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(fsm *f, u_char *inpacket, int l) +{ + u_char *inp = inpacket; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.\n", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.\n", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.\n", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == LS_INITIAL || f->state == LS_STARTING ) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d (%s).\n", + f->protocol, f->state, ppperr_strerr[f->state])); + return; + } + FSMDEBUG((LOG_INFO, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode || + !(*f->callbacks->extcode)(f, code, id, inp, len) ) { + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + } + break; + } +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(fsm *f) +{ + switch( f->state ) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_CLOSED: + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_STOPPED: + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_STOPPING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(void *arg) +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case LS_CLOSING: + case LS_STOPPING: + if( f->retransmits <= 0 ) { + FSMDEBUG((LOG_WARNING, "%s: timeout sending Terminate-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG((LOG_WARNING, "%s: timeout resending Terminate-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + if (f->retransmits <= 0) { + FSMDEBUG((LOG_WARNING, "%s: timeout sending Config-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + f->state = LS_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG((LOG_WARNING, "%s: timeout resending Config-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) { + (*f->callbacks->retransmit)(f); + } + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) +{ + int code, reject_if_disagree; + + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + switch( f->state ) { + case LS_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case LS_CLOSING: + case LS_STOPPING: + return; + + case LS_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case LS_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci) { /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) { + code = CONFREJ; /* Reject all CI */ + } else { + code = CONFACK; + } + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, (u_char)code, id, inp, len); + + if (code == CONFACK) { + if (f->state == LS_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + } else { + f->state = LS_ACKSENT; + } + f->nakloops = 0; + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != LS_ACKRCVD) { + f->state = LS_REQSENT; + } + if( code == CONFNAK ) { + ++f->nakloops; + } + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(fsm *f, int id, u_char *inp, int len) +{ + FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { + /* Ack is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)\n", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + f->state = LS_ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case LS_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) +{ + int (*proc) (fsm *, u_char *, int); + int ret; + + FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !((ret = proc(f, inp, len)))) { + /* Nak/reject is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + case LS_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) { + f->state = LS_STOPPED; /* kludge for stopping CCP */ + } else { + fsm_sconfreq(f, 0); /* Send Configure-Request */ + } + break; + + case LS_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(fsm *f, int id, u_char *p, int len) +{ + LWIP_UNUSED_ARG(p); + + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_REQSENT; /* Start over but keep trying */ + break; + + case LS_OPENED: + if (len > 0) { + FSMDEBUG((LOG_INFO, "%s terminated by peer (%x)\n", PROTO_NAME(f), p)); + } else { + FSMDEBUG((LOG_INFO, "%s terminated by peer\n", PROTO_NAME(f))); + } + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + f->retransmits = 0; + f->state = LS_STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(fsm *f) +{ + FSMDEBUG((LOG_INFO, "fsm_rtermack(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_ACKRCVD: + f->state = LS_REQSENT; + break; + + case LS_OPENED: + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(fsm *f, u_char *inp, int len) +{ + u_char code, id; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + FSMDEBUG((LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d\n", + PROTO_NAME(f), code, id)); + + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(fsm *f, int retransmit) +{ + u_char *outp; + int cilen; + + if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) { + (*f->callbacks->resetci)(f); + } + f->nakloops = 0; + } + + if( !retransmit ) { + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ) { + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { + cilen = peer_mru[f->unit] - HEADERLEN; + } + if (f->callbacks->addci) { + (*f->callbacks->addci)(f, outp, &cilen); + } + } else { + cilen = 0; + } + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d\n", + PROTO_NAME(f), f->reqid)); +} + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/fsm.h b/lab/net/lwip/netif/ppp/fsm.h new file mode 100644 index 0000000..14034ec --- /dev/null +++ b/lab/net/lwip/netif/ppp/fsm.h @@ -0,0 +1,169 @@ +/***************************************************************************** +* fsm.h - Network Control Protocol Finite State Machine header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD code. +*****************************************************************************/ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: fsm.h,v 1.4 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef FSM_H +#define FSM_H + +/***************************************************************************** +************************* PUBLIC DEFINITIONS ********************************* +*****************************************************************************/ +/* + * LCP Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + +/* + * Link states. + */ +#define LS_INITIAL 0 /* Down, hasn't been opened */ +#define LS_STARTING 1 /* Down, been opened */ +#define LS_CLOSED 2 /* Up, hasn't been opened */ +#define LS_STOPPED 3 /* Open, waiting for down event */ +#define LS_CLOSING 4 /* Terminating the connection, not open */ +#define LS_STOPPING 5 /* Terminating, but open */ +#define LS_REQSENT 6 /* We've sent a Config Request */ +#define LS_ACKRCVD 7 /* We've received a Config Ack */ +#define LS_ACKSENT 8 /* We've sent a Config Ack */ +#define LS_OPENED 9 /* Connection available */ + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/***************************************************************************** +************************* PUBLIC DATA TYPES ********************************** +*****************************************************************************/ +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + u_short protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks* callbacks; /* Callback routines */ + char* term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci)(fsm*); /* Reset our Configuration Information */ + int (*cilen)(fsm*); /* Length of our Configuration Information */ + void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */ + int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */ + int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */ + int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */ + int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */ + void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */ + void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */ + void (*starting)(fsm*); /* Called when we want the lower layer */ + void (*finished)(fsm*); /* Called when we don't want the lower layer */ + void (*protreject)(int); /* Called when Protocol-Reject received */ + void (*retransmit)(fsm*); /* Retransmission is necessary */ + int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/***************************************************************************** +*********************** PUBLIC DATA STRUCTURES ******************************* +*****************************************************************************/ +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ + + +/***************************************************************************** +************************** PUBLIC FUNCTIONS ********************************** +*****************************************************************************/ + +/* + * Prototypes + */ +void fsm_init (fsm*); +void fsm_lowerup (fsm*); +void fsm_lowerdown (fsm*); +void fsm_open (fsm*); +void fsm_close (fsm*, char*); +void fsm_input (fsm*, u_char*, int); +void fsm_protreject (fsm*); +void fsm_sdata (fsm*, u_char, u_char, u_char*, int); + +#endif /* FSM_H */ diff --git a/lab/net/lwip/netif/ppp/ipcp.c b/lab/net/lwip/netif/ppp/ipcp.c new file mode 100644 index 0000000..3a403a0 --- /dev/null +++ b/lab/net/lwip/netif/ppp/ipcp.c @@ -0,0 +1,1440 @@ +/***************************************************************************** +* ipcp.c - Network PPP IP Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "auth.h" +#include "fsm.h" +#include "vj.h" +#include "ipcp.h" + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ +/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci (fsm *); /* Reset our CI */ +static int ipcp_cilen (fsm *); /* Return length of our CI */ +static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */ +static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ +static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */ +static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ +static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ +static void ipcp_up (fsm *); /* We're UP */ +static void ipcp_down (fsm *); /* We're DOWN */ +#if 0 +static void ipcp_script (fsm *, char *); /* Run an up/down script */ +#endif +static void ipcp_finished (fsm *); /* Don't need lower layer */ + +/* + * Protocol entry points from main code. + */ +static void ipcp_init (int); +static void ipcp_open (int); +static void ipcp_close (int, char *); +static void ipcp_lowerup (int); +static void ipcp_lowerdown (int); +static void ipcp_input (int, u_char *, int); +static void ipcp_protrej (int); + +static void ipcp_clear_addrs (int); + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +/* global vars */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if 0 + ipcp_printpkt, + NULL, +#endif + 1, + "IPCP", +#if 0 + ip_check_options, + NULL, + ip_active_pkt +#endif +}; + + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +/* local vars */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches LS_OPENED state */ + ipcp_down, /* Called when fsm leaves LS_OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +/* + * Non-standard inet_ntoa left here for compat with original ppp + * sources. Assumes u32_t instead of struct in_addr. + */ + +char * +_inet_ntoa(u32_t n) +{ + struct in_addr ia; + ia.s_addr = n; + return inet_ntoa(ia); +} + +#define inet_ntoa _inet_ntoa + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(int unit) +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->ouraddr = 0; +#if VJ_SUPPORT + wo->neg_vj = 1; +#else /* VJ_SUPPORT */ + wo->neg_vj = 0; +#endif /* VJ_SUPPORT */ + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_SLOTS - 1; + wo->cflag = 0; + wo->default_route = 1; + + ao->neg_addr = 1; +#if VJ_SUPPORT + ao->neg_vj = 1; +#else /* VJ_SUPPORT */ + ao->neg_vj = 0; +#endif /* VJ_SUPPORT */ + ao->maxslotindex = MAX_SLOTS - 1; + ao->cflag = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(int unit) +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(int unit, char *reason) +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(int unit) +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(int unit, u_char *p, int len) +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(fsm *f) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) { + wo->accept_local = 1; + } + if (wo->hisaddr == 0) { + wo->accept_remote = 1; + } + /* Request DNS addresses from the peer */ + wo->req_dns1 = ppp_settings.usepeerdns; + wo->req_dns2 = ppp_settings.usepeerdns; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(fsm *f) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else { \ + neg = 0; \ + } \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETSHORT(cishort, p); \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) { \ + goto bad; \ + } \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u32_t l; \ + if ((len -= addrlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) { \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + return (1); + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!\n")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else { \ + ciaddr2 = 0; \ + } \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s\n", + inet_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG((LOG_INFO, "remote IP address %s\n", + inet_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) { + try.maxslotindex = cimaxslotindex; + } + if (!cicflag) { + try.cflag = 0; + } + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + IPCPDEBUG((LOG_INFO, "primary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + try.dnsaddr[1] = cidnsaddr; + IPCPDEBUG((LOG_INFO, "secondary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + goto bad; + } + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) { + goto bad; + } + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) { + try.hisaddr = ciaddr2; + } + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) { + goto bad; + } + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + if (try.ouraddr != 0) { + try.neg_addr = 1; + } + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) { \ + goto bad; \ + } \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) { \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; +#ifdef OLD_CI_ADDRS + ipcp_options *go = &ipcp_gotoptions[f->unit]; +#endif + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u32_t tl, ciaddr1; /* Parsed address values */ +#ifdef OLD_CI_ADDRS + u32_t ciaddr2; /* Parsed address values */ +#endif + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ +#ifdef OLD_CI_ADDRS /* Need to save space... */ + case CI_ADDRS: + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received ADDRS\n")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "his addr %s\n", inet_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG((LOG_INFO, "our addr %s\n", inet_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; +#endif + + case CI_ADDR: + if (!ao->neg_addr) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR bad len\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting DNS%d Request\n", d+1)); + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking DNS%d Request %d\n", + d+1, inet_ntoa(tl))); + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received DNS%d Request\n", d+1)); + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received WINS%d Request\n", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ cflag %d\n", cflag)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_SLOTS - 1; + ho->cflag = 1; + } + IPCPDEBUG((LOG_INFO, + "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", + ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); + break; + + default: + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting unknown CI type %d\n", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting too many naks\n")); + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) { + BCOPY(cip, ucp, cilen); /* Move it */ + } + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Requesting peer address\n")); + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = (int)(ucp - inp); /* Compute output length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options(u_long localAddr) +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Load our default IP address but allow the remote host to give us + * a new address. + */ + if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { + wo->accept_local = 1; /* don't insist on this default value */ + wo->ouraddr = htonl(localAddr); + } +} +#endif + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void +ipcp_up(fsm *f) +{ + u32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG((LOG_INFO, "ipcp: up\n")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) { + ho->hisaddr = wo->hisaddr; + } + + if (ho->hisaddr == 0) { + IPCPDEBUG((LOG_ERR, "Could not determine remote IP address\n")); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + IPCPDEBUG((LOG_ERR, "Could not determine local IP address\n")); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + IPCPDEBUG((LOG_ERR, "Peer is not authorized to use remote address %s\n", + inet_ntoa(ho->hisaddr))); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) { + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) { + default_route_set[f->unit] = 1; + } + } + + IPCPDEBUG((LOG_NOTICE, "local IP address %s\n", inet_ntoa(go->ouraddr))); + IPCPDEBUG((LOG_NOTICE, "remote IP address %s\n", inet_ntoa(ho->hisaddr))); + if (go->dnsaddr[0]) { + IPCPDEBUG((LOG_NOTICE, "primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); + } + if (go->dnsaddr[1]) { + IPCPDEBUG((LOG_NOTICE, "secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); + } +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(fsm *f) +{ + IPCPDEBUG((LOG_INFO, "ipcp: down\n")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + sifdown(f->unit); + ipcp_clear_addrs(f->unit); +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, etc. + */ +static void +ipcp_clear_addrs(int unit) +{ + u32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(fsm *f) +{ + np_finished(f->unit, PPP_IP); +} + +#if 0 +static int +ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(u_char *pkt, int len) +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) { + return 0; + } + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) { + return 0; + } + if (get_ipproto(pkt) != IPPROTO_TCP) { + return 1; + } + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) { + return 0; + } + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) { + return 0; + } + return 1; +} +#endif + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/ipcp.h b/lab/net/lwip/netif/ppp/ipcp.h new file mode 100644 index 0000000..dfcf4fb --- /dev/null +++ b/lab/net/lwip/netif/ppp/ipcp.h @@ -0,0 +1,124 @@ +/***************************************************************************** +* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ipcp.h,v 1.3 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef IPCP_H +#define IPCP_H + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_WINS1 128 /* Primary WINS value */ +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS2 130 /* Secondary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option */ + + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +typedef struct ipcp_options { + u_int neg_addr : 1; /* Negotiate IP Address? */ + u_int old_addrs : 1; /* Use old (IP-Addresses) option? */ + u_int req_addr : 1; /* Ask peer to send IP address? */ + u_int default_route : 1; /* Assign default route through interface? */ + u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + u_int neg_vj : 1; /* Van Jacobson Compression? */ + u_int old_vj : 1; /* use old (short) form of VJ option? */ + u_int accept_local : 1; /* accept peer's value for ouraddr */ + u_int accept_remote : 1; /* accept peer's value for hisaddr */ + u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */ + u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex; /* VJ slots - 1. */ + u_char cflag; /* VJ slot compression flag. */ + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +} ipcp_options; + + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +extern struct protent ipcp_protent; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +#endif /* IPCP_H */ diff --git a/lab/net/lwip/netif/ppp/lcp.c b/lab/net/lwip/netif/ppp/lcp.c new file mode 100644 index 0000000..85a0add --- /dev/null +++ b/lab/net/lwip/netif/ppp/lcp.c @@ -0,0 +1,2035 @@ +/***************************************************************************** +* lcp.c - Network Link Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "chap.h" +#include "magic.h" +#include "auth.h" +#include "lcp.h" + +#include + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci (fsm*); /* Reset our CI */ +static int lcp_cilen (fsm*); /* Return length of our CI */ +static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */ +static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */ +static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */ +static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */ +static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */ +static void lcp_up (fsm*); /* We're UP */ +static void lcp_down (fsm*); /* We're DOWN */ +static void lcp_starting (fsm*); /* We need lower layer up */ +static void lcp_finished (fsm*); /* We need lower layer down */ +static int lcp_extcode (fsm*, int, u_char, u_char*, int); + +static void lcp_rprotrej (fsm*, u_char*, int); + +/* + * routines to send LCP echos to peer + */ +static void lcp_echo_lowerup (int); +static void lcp_echo_lowerdown (int); +static void LcpEchoTimeout (void*); +static void lcp_received_echo_reply (fsm*, int, u_char*, int); +static void LcpSendEchoRequest (fsm*); +static void LcpLinkFailure (fsm*); +static void LcpEchoCheck (fsm*); + +/* + * Protocol entry points. + * Some of these are called directly. + */ +static void lcp_input (int, u_char *, int); +static void lcp_protrej (int); + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ") + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +/* global vars */ +LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ + + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ +static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches LS_OPENED state */ + lcp_down, /* Called when fsm leaves LS_OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if 0 + lcp_printpkt, + NULL, +#endif + 1, + "LCP", +#if 0 + NULL, + NULL, + NULL +#endif +}; + +int lcp_loopbackfail = DEFLOOPBACKFAIL; + + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * lcp_init - Initialize LCP. + */ +void +lcp_init(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */ + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + ao->neg_chap = (CHAP_SUPPORT != 0); + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = (PAP_SUPPORT != 0); + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + ao->neg_cbcp = (CBCP_SUPPORT != 0); + + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][15] = 0x60; + xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF)); + xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF); + xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF); + xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG((LOG_INFO, "lcp_init: xmit_accm=%X %X %X %X\n", + xmit_accm[unit][0], + xmit_accm[unit][1], + xmit_accm[unit][2], + xmit_accm[unit][3])); + + lcp_phase[unit] = PHASE_INITIALIZE; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) { + f->flags |= OPT_PASSIVE; + } + if (wo->silent) { + f->flags |= OPT_SILENT; + } + fsm_open(f); + + lcp_phase[unit] = PHASE_ESTABLISH; +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(int unit, char *reason) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do an + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = LS_CLOSED; + lcp_finished(f); + } else { + fsm_close(&lcp_fsm[unit], reason); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(int unit) +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, &xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0x00000000l, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0] + | ((u_long)xmit_accm[unit][1] << 8) + | ((u_long)xmit_accm[unit][2] << 16) + | ((u_long)xmit_accm[unit][3] << 24); + LCPDEBUG((LOG_INFO, "lcp_lowerup: asyncmap=%X %X %X %X\n", + xmit_accm[unit][3], + xmit_accm[unit][2], + xmit_accm[unit][1], + xmit_accm[unit][0])); + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(int unit) +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(int unit, u_char *p, int len) +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the LS_OPENED state. + */ + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); +} + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(int unit, u_char *p, int len) +{ + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len) +{ + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != LS_OPENED) { + break; + } + LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d\n", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(fsm *f, u_char *inp, int len) +{ + int i; + struct protent *protp; + u_short prot; + + if (len < sizeof (u_short)) { + LCPDEBUG((LOG_INFO, "lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG((LOG_INFO, "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * LS_OPENED state SHOULD be silently discarded. + */ + if( f->state != LS_OPENED ) { + LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d\n", f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + } + + LCPDEBUG((LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x\n", prot)); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +static void +lcp_protrej(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, "lcp_protrej: Received Protocol-Reject for LCP!\n")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(fsm *f) +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int lcp_cilen(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: opt=%d\n", opt)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: INT opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: CHAP opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: L opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: LQR opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + LCPDEBUG((LOG_ERR, "Bug in lcp_addci: wrong length\n")); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + LCPDEBUG((LOG_INFO, "lcp_acki: Ack\n")); + return (1); +bad: + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!\n")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < PPP_DEFMRU) { + try.mru = cishort; + } + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) { + goto bad; + } + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) { + try.neg_chap = 0; + } else { + try.neg_upap = 0; + } + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) { + try.neg_lqr = 0; + } else { + try.lqr_period = cilong; + } + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) { + goto bad; + } + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + || no.neg_asyncmap || cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) { + goto bad; + } + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) { + goto bad; + } + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + LCPDEBUG((LOG_NOTICE, "Serial line is looped back.\n")); + lcp_close(f->unit, "Loopback detected"); + } + } else { + try.numloops = 0; + } + *go = try; + } + + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO, "lcp_rejci: void opt %d rejected\n", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: short opt %d rejected\n", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) { \ + goto bad; \ + } \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: chap opt %d rejected\n", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: long opt %d rejected\n", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: LQR opt %d rejected\n", opt)); \ + } +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: Callback opt %d rejected\n", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(fsm *f, + u_char *inp, /* Requested CIs */ + int *lenp, /* Length of requested CIs */ + int reject_if_disagree) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype, cichar; /* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + u_char *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ +#if TRACELCP > 0 + char traceBuf[80]; + int traceNdx = 0; +#endif + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru) { /* Allow option? */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_SHORT) { /* Check CI length */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - bad length\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Nak - MRU too small\n")); + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_LONG) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP bad length\n")); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", + cilong, ao->asyncmap)); + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE missing arg\n")); + orc = CONFREJ; + break; + } else if (!(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE not allowed\n")); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap) { /* we've already accepted CHAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_SHORT) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap) { /* we've already accepted PAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_CHAP) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#ifdef CHAPMS + && cichar != CHAP_MICROSOFT +#endif + ) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", cichar)); + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, cichar); + traceNdx = strlen(traceBuf); +#endif + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + GETSHORT(cishort, p); + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); + traceNdx = strlen(traceBuf); +#endif + + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); + traceNdx = strlen(traceBuf); +#endif + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + case CI_MRRU: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_SSNHF: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_EPDISC: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + default: +#if TRACELCP + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + } + + endswitch: +#if TRACELCP + if (traceNdx >= 80 - 32) { + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd%s\n", traceBuf)); + traceNdx = 0; + } +#endif + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) { /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + } + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = (int)(next - inp); + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = (int)(nakp - nak_buffer); + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = (int)(rejp - inp); + break; + } + +#if TRACELCP > 0 + if (traceNdx > 0) { + LCPDEBUG((LOG_INFO, "lcp_reqci: %s\n", traceBuf)); + } +#endif + LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void +lcp_up(fsm *f) +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) { + go->magicnumber = 0; + } + if (!ho->neg_magicnumber) { + ho->magicnumber = 0; + } + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) { + peer_mru[f->unit] = ho->mru; + } + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(fsm *f) +{ + link_required(f->unit); +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(fsm *f) +{ + link_terminated(f->unit); +} + + +#if 0 +/* + * print_string - print a readable representation of a string using + * printer. + */ +static void +print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg) +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') { + printer(arg, "\\"); + } + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + } + } + } + printer(arg, "\""); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) { + return 0; + } + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) { + printer(arg, " %s", lcp_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%lx", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string((char*)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return (int)(p - pstart); +} +#endif + +/* + * Time to shut down the link because there is nothing out there. + */ +static void +LcpLinkFailure (fsm *f) +{ + if (f->state == LS_OPENED) { + LCPDEBUG((LOG_INFO, "No response to %d echo-requests\n", lcp_echos_pending)); + LCPDEBUG((LOG_NOTICE, "Serial link appears to be disconnected.\n")); + lcp_close(f->unit, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ +static void +LcpEchoCheck (fsm *f) +{ + LcpSendEchoRequest (f); + + /* + * Start the timer for the next interval. + */ + LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + + TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ +static void +LcpEchoTimeout (void *arg) +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ +static void +lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) +{ + u32_t magic; + + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + LCPDEBUG((LOG_WARNING, "lcp: received short Echo-Reply, length %d\n", len)); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { + LCPDEBUG((LOG_WARNING, "appear to have received our own echo-reply!\n")); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ +static void +LcpSendEchoRequest (fsm *f) +{ + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending++ >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == LS_OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) { + LcpEchoCheck (f); + } +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/lcp.h b/lab/net/lwip/netif/ppp/lcp.h new file mode 100644 index 0000000..1a5e5a4 --- /dev/null +++ b/lab/net/lwip/netif/ppp/lcp.h @@ -0,0 +1,167 @@ +/***************************************************************************** +* lcp.h - Network Link Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: lcp.h,v 1.3 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef LCP_H +#define LCP_H + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ + +/* + * LCP-specific packet types. + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + u_int passive : 1; /* Don't die if we don't get a response */ + u_int silent : 1; /* Wait for the other end to start first */ + u_int restart : 1; /* Restart vs. exit after close */ + u_int neg_mru : 1; /* Negotiate the MRU? */ + u_int neg_asyncmap : 1; /* Negotiate the async map? */ + u_int neg_upap : 1; /* Ask for UPAP authentication? */ + u_int neg_chap : 1; /* Ask for CHAP authentication? */ + u_int neg_magicnumber : 1; /* Ask for magic number? */ + u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u_int neg_cbcp : 1; /* Negotiate use of CBCP */ +#ifdef PPP_MULTILINK + u_int neg_mrru : 1; /* Negotiate multilink MRRU */ + u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */ + u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */ +#endif + u_short mru; /* Value of MRU */ +#ifdef PPP_MULTILINK + u_short mrru; /* Value of MRRU, and multilink enable */ +#endif + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#ifdef PPP_MULTILINK + struct epdisc endpoint; /* endpoint discriminator */ +#endif +} lcp_options; + +/* + * Values for phase from BSD pppd.h based on RFC 1661. + */ +typedef enum { + PHASE_DEAD = 0, + PHASE_INITIALIZE, + PHASE_ESTABLISH, + PHASE_AUTHENTICATE, + PHASE_CALLBACK, + PHASE_NETWORK, + PHASE_TERMINATE +} LinkPhase; + + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern ext_accm xmit_accm[]; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +void lcp_init (int); +void lcp_open (int); +void lcp_close (int, char *); +void lcp_lowerup (int); +void lcp_lowerdown(int); +void lcp_sprotrej (int, u_char *, int); /* send protocol reject */ + +extern struct protent lcp_protent; + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 + +#endif /* LCP_H */ diff --git a/lab/net/lwip/netif/ppp/magic.c b/lab/net/lwip/netif/ppp/magic.c new file mode 100644 index 0000000..d3922bb --- /dev/null +++ b/lab/net/lwip/netif/ppp/magic.c @@ -0,0 +1,82 @@ +/***************************************************************************** +* magic.c - Network Random Number Generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD magic.c. +*****************************************************************************/ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#include "ppp.h" +#include "randm.h" +#include "magic.h" + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * magicInit - Initialize the magic number generator. + * + * Since we use another random number generator that has its own + * initialization, we do nothing here. + */ +void magicInit() +{ + return; +} + +/* + * magic - Returns the next magic number. + */ +u32_t magic() +{ + return avRandom(); +} + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/magic.h b/lab/net/lwip/netif/ppp/magic.h new file mode 100644 index 0000000..bc51749 --- /dev/null +++ b/lab/net/lwip/netif/ppp/magic.h @@ -0,0 +1,67 @@ +/***************************************************************************** +* magic.h - Network Random Number Generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: magic.h,v 1.2 2007/12/02 22:35:55 fbernon Exp $ + */ + +#ifndef MAGIC_H +#define MAGIC_H + +/***************************************************************************** +************************** PUBLIC FUNCTIONS ********************************** +*****************************************************************************/ + +/* Initialize the magic number generator */ +void magicInit(void); + +/* Returns the next magic number */ +u32_t magic(void); + +#endif /* MAGIC_H */ diff --git a/lab/net/lwip/netif/ppp/md5.c b/lab/net/lwip/netif/ppp/md5.c new file mode 100644 index 0000000..d65eced --- /dev/null +++ b/lab/net/lwip/netif/ppp/md5.c @@ -0,0 +1,318 @@ +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT || MD5_SUPPORT + +#include "ppp.h" +#include "pppdebug.h" + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (u32_t *buf, u32_t *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##UL +#else +#ifdef WIN32 +#define UL(x) x##UL +#else +#define UL(x) x +#endif +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void +MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (u32_t)0; + + /* Load magic initialization constants. */ + mdContext->buf[0] = (u32_t)0x67452301UL; + mdContext->buf[1] = (u32_t)0xefcdab89UL; + mdContext->buf[2] = (u32_t)0x98badcfeUL; + mdContext->buf[3] = (u32_t)0x10325476UL; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void +MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + +#if 0 + ppp_trace(LOG_INFO, "MD5Update: %u:%.*H\n", inLen, MIN(inLen, 20) * 2, inBuf); + ppp_trace(LOG_INFO, "MD5Update: %u:%s\n", inLen, inBuf); +#endif + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) { + mdContext->i[1]++; + } + mdContext->i[0] += ((u32_t)inLen << 3); + mdContext->i[1] += ((u32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void +MD5Final (unsigned char hash[], MD5_CTX *mdContext) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + SMEMCPY(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void +Transform (u32_t *buf, u32_t *in) +{ + u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif /* CHAP_SUPPORT || MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/md5.h b/lab/net/lwip/netif/ppp/md5.h new file mode 100644 index 0000000..e129533 --- /dev/null +++ b/lab/net/lwip/netif/ppp/md5.h @@ -0,0 +1,55 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef MD5_H +#define MD5_H + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + u32_t i[2]; /* number of _bits_ handled mod 2^64 */ + u32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init ( MD5_CTX *mdContext); +void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final ( unsigned char hash[], MD5_CTX *mdContext); + +#endif /* MD5_H */ diff --git a/lab/net/lwip/netif/ppp/pap.c b/lab/net/lwip/netif/ppp/pap.c new file mode 100644 index 0000000..b38abd1 --- /dev/null +++ b/lab/net/lwip/netif/ppp/pap.c @@ -0,0 +1,617 @@ +/***************************************************************************** +* pap.c - Network Password Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-12 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "auth.h" +#include "pap.h" + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Protocol entry points. + */ +static void upap_init (int); +static void upap_lowerup (int); +static void upap_lowerdown (int); +static void upap_input (int, u_char *, int); +static void upap_protrej (int); + +static void upap_timeout (void *); +static void upap_reqtimeout(void *); +static void upap_rauthreq (upap_state *, u_char *, int, int); +static void upap_rauthack (upap_state *, u_char *, int, int); +static void upap_rauthnak (upap_state *, u_char *, int, int); +static void upap_sauthreq (upap_state *); +static void upap_sresp (upap_state *, u_char, u_char, char *, int); + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if 0 + upap_printpkt, + NULL, +#endif + 1, + "PAP", +#if 0 + NULL, + NULL, + NULL +#endif +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Set the default login name and password for the pap sessions + */ +void +upap_setloginpasswd(int unit, const char *luser, const char *lpassword) +{ + upap_state *u = &upap[unit]; + + /* Save the username and password we're given */ + u->us_user = luser; + u->us_userlen = strlen(luser); + u->us_passwd = lpassword; + u->us_passwdlen = strlen(lpassword); +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(int unit, char *user, char *password) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_authwithpeer: %d user=%s password=%s s=%d\n", + unit, user, password, u->us_clientstate)); + + upap_setloginpasswd(unit, user, password); + + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(int unit) +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } +} + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_init: %d\n", unit)); + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + UPAPDEBUG((LOG_INFO, "upap_timeout: %d timeout %d expired s=%d\n", + u->us_unit, u->us_timeouttime, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { + return; + } + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + UPAPDEBUG((LOG_ERR, "No response to PAP authenticate-requests\n")); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request */ +} + + +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void +upap_reqtimeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) { + return; /* huh?? */ + } + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void +upap_lowerup(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_lowerup: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_INITIAL) { + u->us_clientstate = UPAPCS_CLOSED; + } else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) { + u->us_serverstate = UPAPSS_CLOSED; + } else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } + } +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +upap_lowerdown(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + } + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void +upap_protrej(int unit) +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + UPAPDEBUG((LOG_ERR, "PAP authentication failed due to protocol-reject\n")); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + UPAPDEBUG((LOG_ERR, "PAP authentication of peer failed (protocol-reject)\n")); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void +upap_input(int unit, u_char *inpacket, int l) +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length.\n")); + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet.\n")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(upap_state *u, u_char *inp, int id, int len) +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + int retcode; + char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.\n", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) { + return; + } + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void +upap_rauthnak(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_BADAUTH; + + UPAPDEBUG((LOG_ERR, "PAP authentication failed\n")); + auth_withpeer_fail(u->us_unit, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(upap_state *u) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf[u->us_unit]; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d\n", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf[u->us_unit]; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); +} + +#if 0 +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static int upap_printpkt( + u_char *p, + int plen, + void (*printer) (void *, char *, ...), + void *arg +) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} +#endif /* 0 */ + +#endif /* PAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/pap.h b/lab/net/lwip/netif/ppp/pap.h new file mode 100644 index 0000000..0a09fc8 --- /dev/null +++ b/lab/net/lwip/netif/ppp/pap.h @@ -0,0 +1,131 @@ +/***************************************************************************** +* pap.h - PPP Password Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef PAP_H +#define PAP_H + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + const char *us_user; /* User */ + int us_userlen; /* User length */ + const char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ +} upap_state; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +extern upap_state upap[]; + +void upap_setloginpasswd(int unit, const char *luser, const char *lpassword); +void upap_authwithpeer (int, char *, char *); +void upap_authpeer (int); + +extern struct protent pap_protent; + +#endif /* PAP_SUPPORT */ + +#endif /* PAP_H */ diff --git a/lab/net/lwip/netif/ppp/ppp.c b/lab/net/lwip/netif/ppp/ppp.c new file mode 100644 index 0000000..a3817da --- /dev/null +++ b/lab/net/lwip/netif/ppp/ppp.c @@ -0,0 +1,2003 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip.h" /* for ip_input() */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "randm.h" +#include "fsm.h" +#if PAP_SUPPORT +#include "pap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap.h" +#endif /* CHAP_SUPPORT */ +#include "ipcp.h" +#include "lcp.h" +#include "magic.h" +#include "auth.h" +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +typedef enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} PPPDevStates; + +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07]) + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +/* + * PPP interface control block. + */ +typedef struct PPPControl_s { + char openFlag; /* True when in use. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ + int if_up; /* True when the interface is up. */ + int errCode; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ + int kill_link; /* Shut the link down. */ + int sig_hup; /* Carrier lost. */ + struct pbuf *inHead, *inTail; /* The input packet. */ + PPPDevStates inState; /* The input process state. */ + char inEscaped; /* Escape next character. */ + u16_t inProtocol; /* The input protocol code. */ + u16_t inFCS; /* Input Frame Check Sequence value. */ +#endif /* PPPOS_SUPPORT */ + int mtu; /* Peer's mru */ + int pcomp; /* Does peer accept protocol compression? */ + int accomp; /* Does peer accept addr/ctl compression? */ + u_long lastXMit; /* Time of last transmission. */ + ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ + ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ +#if PPPOS_SUPPORT && VJ_SUPPORT + int vjEnabled; /* Flag indicating VJ compression enabled. */ + struct vjcompress vjComp; /* Van Jabobsen compression header. */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + struct netif netif; + + struct ppp_addrs addrs; + + void (*linkStatusCB)(void *ctx, int errCode, void *arg); + void *linkStatusCtx; + +} PPPControl; + + +/* + * Ioctl definitions. + */ + +struct npioctl { + int protocol; /* PPP procotol, e.g. PPP_IP */ + enum NPmode mode; +}; + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +#if PPPOS_SUPPORT +static void pppMain(void *pd); +static void pppDrop(PPPControl *pc); +static void pppInProc(int pd, u_char *s, int l); +#endif /* PPPOS_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +u_long subnetMask; + +static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +struct protent *ppp_protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ + &ipcp_protent, +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ + NULL +}; + + +/* + * Buffers for outgoing packets. This must be accessed only from the appropriate + * PPP task so that it doesn't need to be protected to avoid collisions. + */ +u_char *outpacket_buf[NUM_PPP]; + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ + +#if PPPOS_SUPPORT +/* + * FCS lookup table as calculated by genfcstab. + */ +static const u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +static u_char pppACCMMask[] = { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80 +}; + + +void +pppMainWakeup(int pd) +{ + PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d\n", pd)); + sio_read_abort(pppControl[pd].fd); +} +#endif /* PPPOS_SUPPORT */ + +void +pppLinkTerminated(int pd) +{ + PPPControl *pc = &pppControl[pd]; + + PPPDEBUG((LOG_DEBUG, "pppLinkTerminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if(pc->ethif) { + pppoe_disconnect(pc->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +void +pppLinkDown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + + PPPDEBUG((LOG_DEBUG, "pppLinkDown: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if(pc->ethif) { + pppoe_disconnect(pc->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +/* these callbacks are necessary because lcp_* functions + must be called in the same context as pppInput(), + namely the tcpip_thread(), essentially because + they manipulate timeouts which are thread-private +*/ + +static void +pppStartCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppStartCB: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ +} + +static void +pppStopCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppStopCB: unit %d\n", pd)); + lcp_close(pd, "User request"); +} + +static void +pppHupCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppHupCB: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* Initialize the PPP subsystem. */ + +struct ppp_settings ppp_settings; + +err_t +pppInit(void) +{ + struct protent *protp; + int i, j; + + memset(&ppp_settings, 0, sizeof(ppp_settings)); + ppp_settings.usepeerdns = 1; + pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL); + + magicInit(); + + for (i = 0; i < NUM_PPP; i++) { + pppControl[i].openFlag = 0; + + subnetMask = htonl(0xffffff00); + + outpacket_buf[i] = (u_char *)mem_malloc(PPP_MRU+PPP_HDRLEN); + if(!outpacket_buf[i]) { + return ERR_MEM; + } + + /* + * Initialize to the standard option set. + */ + for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { + (*protp->init)(i); + } + } + +#if LINK_STATS + /** @todo already done in stats_init (in fact, zeroed at boot). So, remove it? */ + /* Clear the statistics. */ + memset(&lwip_stats.link, 0, sizeof(lwip_stats.link)); +#endif /* LINK_STATS */ + +#if PPPOE_SUPPORT + pppoe_init(); +#endif /* PPPOE_SUPPORT */ + + return ERR_OK; +} + +void +pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) +{ + switch(authType) { + case PPPAUTHTYPE_NONE: + default: +#ifdef LWIP_PPP_STRICT_PAP_REJECT + ppp_settings.refuse_pap = 1; +#else /* LWIP_PPP_STRICT_PAP_REJECT */ + /* some providers request pap and accept an empty login/pw */ + ppp_settings.refuse_pap = 0; +#endif /* LWIP_PPP_STRICT_PAP_REJECT */ + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_ANY: + /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 0; + break; + + case PPPAUTHTYPE_PAP: + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_CHAP: + ppp_settings.refuse_pap = 1; + ppp_settings.refuse_chap = 0; + break; + } + + if(user) { + strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); + ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; + } else { + ppp_settings.user[0] = '\0'; + } + + if(passwd) { + strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); + ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; + } else { + ppp_settings.passwd[0] = '\0'; + } +} + +#if PPPOS_SUPPORT +/* Open a new PPP connection using the given I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. If this port + * connects to a modem, the modem connection must be + * established before calling this. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. */ +int +pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pppControl[pd].openFlag = !0; + } + + /* Launch a deamon thread. */ + if (pd >= 0) { + pppControl[pd].openFlag = 1; + + lcp_init(pd); + pc = &pppControl[pd]; + pc->fd = fd; +#if PPPOE_SUPPORT + pc->ethif= NULL; +#endif /* PPPOE_SUPPORT */ + pc->kill_link = 0; + pc->sig_hup = 0; + pc->if_up = 0; + pc->errCode = 0; + pc->inState = PDIDLE; + pc->inHead = NULL; + pc->inTail = NULL; + pc->inEscaped = 0; + pc->lastXMit = 0; + +#if VJ_SUPPORT + pc->vjEnabled = 0; + vj_compress_init(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + memset(pc->inACCM, 0, sizeof(ext_accm)); + pc->inACCM[15] = 0x60; + memset(pc->outACCM, 0, sizeof(ext_accm)); + pc->outACCM[15] = 0x60; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + sys_thread_new(PPP_THREAD_NAME, pppMain, (void*)pd, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); + if(!linkStatusCB) { + while(pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + pppClose(pd); + if (pc->errCode) { + pd = pc->errCode; + } else { + pd = PPPERR_CONNECT; + } + } + } + } + } + + return pd; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); + +void +pppOverEthernetClose(int pd) +{ + PPPControl* pc = &pppControl[pd]; + + /* *TJL* There's no lcp_deinit */ + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); +} + +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pppControl[pd].openFlag = !0; + } + + /* Launch a deamon thread. */ + if (pd >= 0) { + + pppControl[pd].openFlag = 1; + + lcp_init(pd); + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + pc = &pppControl[pd]; + pc->if_up = 0; + pc->errCode = 0; + pc->lastXMit = 0; +#if PPPOS_SUPPORT + pc->kill_link = 0; + pc->sig_hup = 0; + pc->inState = PDIDLE; + pc->inHead = NULL; + pc->inTail = NULL; + pc->inEscaped = 0; +#if VJ_SUPPORT + pc->vjEnabled = 0; +#endif /* VJ_SUPPORT */ +#endif /* PPPOS_SUPPORT */ + pc->ethif= ethif; + + memset(pc->inACCM, 0, sizeof(ext_accm)); + memset(pc->outACCM, 0, sizeof(ext_accm)); + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->openFlag = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + + if(!linkStatusCB) { + while(pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + pppClose(pd); + if (pc->errCode) { + pd = pc->errCode; + } else { + pd = PPPERR_CONNECT; + } + } + } + } + } + + return pd; +} +#endif /* PPPOE_SUPPORT */ + + +/* Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. */ +int +pppClose(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "pppClose: unit %d kill_link -> pppStopCB\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + tcpip_callback(pppStopCB, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pc->kill_link = !0; + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } + + if(!pc->linkStatusCB) { + while(st >= 0 && lcp_phase[pd] != PHASE_DEAD) { + sys_msleep(500); + break; + } + } + + return st; +} + +/* This function is called when carrier is lost on the PPP channel. */ +void +pppSigHUP(int pd) +{ + PPPControl *pc = &pppControl[pd]; + +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + tcpip_callback(pppHupCB, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pc->sig_hup = 1; + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +#if PPPOS_SUPPORT +static void +nPut(PPPControl *pc, struct pbuf *nb) +{ + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { + if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { + PPPDEBUG((LOG_WARNING, + "PPP nPut: incomplete sio_write(%d,, %u) = %d\n", pc->fd, b->len, c)); + LINK_STATS_INC(link.err); + pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */ + break; + } + } + + pbuf_free(nb); + LINK_STATS_INC(link.xmit); +} + +/* + * pppAppend - append given character to end of given pbuf. If outACCM + * is not NULL and the character needs to be escaped, do so. + * If pbuf is full, append another. + * Return the current pbuf. + */ +static struct pbuf * +pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) +{ + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (tb) { + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (outACCM && ESCAPE_P(*outACCM, c)) { + *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u_char*)nb->payload + nb->len++) = c; + } + } + + return tb; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + u_short protocol = PPP_IP; + int i=0; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return ERR_MEM; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->lastXMit = sys_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + return PPPERR_DEVICE; + } + + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ + +/* Send a packet on the given connection. */ +static err_t +pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *ipaddr) +{ + int pd = (int)netif->state; + u_short protocol = PPP_IP; + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_int fcsOut = PPP_INITFCS; + struct pbuf *headMB = NULL, *tailMB = NULL, *p; + u_char c; +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(ipaddr); + + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) { + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad parms prot=%d pb=%p\n", + pd, protocol, pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (lcp_phase[pd] == PHASE_DEAD) { + PPPDEBUG((LOG_ERR, "pppifOutput[%d]: link not up\n", pd)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + return ERR_RTE; + } + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + /* Grab an output buffer. */ + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: first alloc fail\n", pd)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return ERR_MEM; + } + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pc->vjEnabled) { + switch (vj_compress_tcp(&pc->vjComp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad IP packet\n", pd)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + pbuf_free(headMB); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + + tailMB = headMB; + + /* Build the PPP header. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + + pc->lastXMit = sys_jiffies(); + if (!pc->accomp) { + fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); + tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM); + fcsOut = PPP_FCS(fcsOut, PPP_UI); + tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM); + } + if (!pc->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + c = protocol & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u_char *sPtr; + + sPtr = (u_char*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. */ + if (!tailMB) { + PPPDEBUG((LOG_WARNING, + "pppifOutput[%d]: Alloc err - dropping proto=%d\n", + pd, protocol)); + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return ERR_MEM; + } + + /* Send it. */ + PPPDEBUG((LOG_INFO, "pppifOutput[%d]: proto=0x%04X\n", pd, protocol)); + + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return ERR_OK; +} + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +int +pppIOCtl(int pd, int cmd, void *arg) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + if (pd < 0 || pd >= NUM_PPP) { + st = PPPERR_PARAM; + } else { + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pc->if_up); + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pc->errCode = *(int *)arg; + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pc->errCode); + } else { + st = PPPERR_PARAM; + } + break; +#if PPPOS_SUPPORT + case PPPCTLG_FD: + if (arg) { + *(sio_fd_t *)arg = pc->fd; + } else { + st = PPPERR_PARAM; + } + break; +#endif /* PPPOS_SUPPORT */ + default: + st = PPPERR_PARAM; + break; + } + } + + return st; +} + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_int +pppMTU(int pd) +{ + PPPControl *pc = &pppControl[pd]; + u_int st; + + /* Validate parameters. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + } else { + st = pc->mtu; + } + + return st; +} + +#if PPPOE_SUPPORT +int +pppWriteOverEthernet(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + n, PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->lastXMit = sys_jiffies(); + + SMEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + return PPPERR_DEVICE; + } + + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written + * -1 Failed to write to device + */ +int +pppWrite(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_char c; + u_int fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppWriteOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + tailMB = headMB; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + pc->lastXMit = sys_jiffies(); + + fcsOut = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tailMB) { + PPPDEBUG((LOG_WARNING, + "pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); + /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + PPPDEBUG((LOG_INFO, "pppWrite[%d]: len=%d\n", pd, headMB->len)); + /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return PPPERR_NONE; +} + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config( int unit, int mtu, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + pc->mtu = mtu; + pc->pcomp = pcomp; + pc->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF); + } + PPPDEBUG((LOG_INFO, "ppp_send_config[%d]: outACCM=%X %X %X %X\n", + unit, + pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3])); +} + + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(int unit, ext_accm *accm) +{ + SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm)); + PPPDEBUG((LOG_INFO, "ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n", + unit, + pppControl[unit].outACCM[0], + pppControl[unit].outACCM[1], + pppControl[unit].outACCM[2], + pppControl[unit].outACCM[3])); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32 / 8; i++) { + pc->inACCM[i] = (u_char)(asyncmap >> (i * 8)); + } + PPPDEBUG((LOG_INFO, "ppp_recv_config[%d]: inACCM=%X %X %X %X\n", + unit, + pc->inACCM[0], pc->inACCM[1], pc->inACCM[2], pc->inACCM[3])); +} + +#if 0 +/* + * ccp_test - ask kernel whether a given compression method + * is acceptable for use. Returns 1 if the method and parameters + * are OK, 0 if the method is known but the parameters are not OK + * (e.g. code size should be reduced), or -1 if the method is unknown. + */ +int +ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr) +{ + return 0; /* XXX Currently no compression. */ +} + +/* + * ccp_flags_set - inform kernel about the current state of CCP. + */ +void +ccp_flags_set(int unit, int isopen, int isup) +{ + /* XXX */ +} + +/* + * ccp_fatal_error - returns 1 if decompression was disabled as a + * result of an error detected after decompression of a packet, + * 0 otherwise. This is necessary because of patent nonsense. + */ +int +ccp_fatal_error(int unit) +{ + /* XXX */ + return 0; +} +#endif + +/* + * get_idle_time - return how long the link has been idle. + */ +int +get_idle_time(int u, struct ppp_idle *ip) +{ + /* XXX */ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(ip); + + return 0; +} + + +/* + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t +GetMask(u32_t addr) +{ + u32_t mask, nmask; + + htonl(addr); + if (IN_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IN_CLASSA_NET; + } else if (IN_CLASSB(addr)) { + nmask = IN_CLASSB_NET; + } else { + nmask = IN_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = subnetMask | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + + return mask; +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp( int pd, int vjcomp, int cidcomp, int maxcid) +{ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPControl *pc = &pppControl[pd]; + + pc->vjEnabled = vjcomp; + pc->vjComp.compressSlot = cidcomp; + pc->vjComp.maxSlotIndex = maxcid; + PPPDEBUG((LOG_INFO, "sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; +} + +/* + * pppifNetifInit - netif init callback + */ +static err_t +pppifNetifInit(struct netif *netif) +{ + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = pppifOutput; + netif->mtu = pppMTU((int)netif->state); + return ERR_OK; +} + + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_remove(&pc->netif); + if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, &pc->addrs.his_ipaddr, (void *)pd, pppifNetifInit, ip_input)) { + netif_set_up(&pc->netif); +#if LWIP_DHCP + /* ugly workaround for storing a reference to the ppp related info*/ + pc->netif.dhcp = (struct dhcp *) &pc->addrs; +#endif /* LWIP_DHCP */ + pc->if_up = 1; + pc->errCode = PPPERR_NONE; + + PPPDEBUG((LOG_DEBUG, "sifup: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs); + } + } else { + st = 0; + PPPDEBUG((LOG_ERR, "sifup[%d]: netif_add failed\n", pd)); + } + } + + return st; +} + +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int +sifnpmode(int u, int proto, enum NPmode mode) +{ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifdown[%d]: bad parms\n", pd)); + } else { + pc->if_up = 0; + netif_remove(&pc->netif); + PPPDEBUG((LOG_DEBUG, "sifdown: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL); + } + } + return st; +} + +/** + * sifaddr - Config the interface IP addresses and netmask. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h His IP address ??? + * @param m IP subnet mask ??? + * @param ns1 Primary DNS + * @param ns2 Secondary DNS + */ +int +sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o)); + SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h)); + SMEMCPY(&pc->addrs.netmask, &m, sizeof(m)); + SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2)); + } + return st; +} + +/** + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h IP broadcast address ??? + */ +int +cifaddr( int pd, u32_t o, u32_t h) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(o); + LWIP_UNUSED_ARG(h); + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); + IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); + } + return st; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(&pc->netif); + } + + /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + + return st; +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(NULL); + } + + return st; +} + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#if PPPOS_SUPPORT +/* The main PPP process function. This implements the state machine according + * to section 4 of RFC 1661: The Point-To-Point Protocol. */ +static void +pppMain(void *arg) +{ + int pd = (int)arg; + struct pbuf *p; + PPPControl* pc; + int c; + + pc = &pppControl[pd]; + + p = pbuf_alloc(PBUF_RAW, PPP_MRU+PPP_HDRLEN, PBUF_RAM); + if (!p) { + LWIP_ASSERT("p != NULL", p); + pc->errCode = PPPERR_ALLOC; + goto out; + } + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + tcpip_callback(pppStartCB, arg); + while (lcp_phase[pd] != PHASE_DEAD) { + if (pc->kill_link) { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d kill_link -> pppStopCB\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + tcpip_callback(pppStopCB, arg); + pc->kill_link = 0; + } else if (pc->sig_hup) { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d sig_hup -> pppHupCB\n", pd)); + pc->sig_hup = 0; + tcpip_callback(pppHupCB, arg); + } else { + c = sio_read(pc->fd, p->payload, p->len); + if(c > 0) { + pppInProc(pd, p->payload, c); + } else { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d sio_read len=%d returned %d\n", pd, p->len, c)); + sys_msleep(1); /* give other tasks a chance to run */ + } + } + } + PPPDEBUG((LOG_INFO, "pppMain: unit %d: PHASE_DEAD\n", pd)); + pppDrop(pc); /* bug fix #17726 */ + pbuf_free(p); + +out: + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } + + pc->openFlag = 0; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + +void +pppOverEthernetInitFailed(void* arg) +{ + PPPControl* pc; + int pd = (int)arg; + + pppHupCB(arg); + pppStopCB(arg); + + pc = &pppControl[pd]; + pppoe_destroy(&pc->netif); + pc->openFlag = 0; + + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } +} + +static void +pppOverEthernetLinkStatusCB(int pd, int up) +{ + if(up) { + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + tcpip_callback(pppStartCB, (void*)pd); + } else { + PPPControl* pc; + pc = &pppControl[pd]; + tcpip_callback(pppOverEthernetInitFailed, (void*)pd); + } +} +#endif /* PPPOE_SUPPORT */ + +struct pbuf * +pppSingleBuf(struct pbuf *p) +{ + struct pbuf *q, *b; + u_char *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG((LOG_ERR, + "pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +struct pppInputHeader { + int unit; + u16_t proto; +}; + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +static void +pppInput(void *arg) +{ + struct pbuf *nb = (struct pbuf *)arg; + u16_t protocol; + int pd; + + pd = ((struct pppInputHeader *)nb->payload)->unit; + protocol = ((struct pppInputHeader *)nb->payload)->proto; + + if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + LINK_STATS_INC(link.recv); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { + if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || + (lcp_phase[pd] != PHASE_AUTHENTICATE)) { + PPPDEBUG((LOG_INFO, "pppInput: discarding proto 0x%04X in phase %d\n", protocol, lcp_phase[pd])); + goto drop; + } + } + + switch(protocol) { + case PPP_VJC_COMP: /* VJ compressed TCP */ +#if VJ_SUPPORT + PPPDEBUG((LOG_INFO, "pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG((LOG_WARNING, "pppInput[%d]: Dropping VJ compressed\n", pd)); +#else /* VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG((LOG_INFO, "pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); +#endif /* VJ_SUPPORT */ + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ +#if VJ_SUPPORT + PPPDEBUG((LOG_INFO, "pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG((LOG_WARNING, "pppInput[%d]: Dropping VJ uncompressed\n", pd)); +#else /* VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG((LOG_INFO, + "pppInput[%d]: drop VJ UnComp in %d:.*H\n", + pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); +#endif /* VJ_SUPPORT */ + break; + + case PPP_IP: /* Internet Protocol */ + PPPDEBUG((LOG_INFO, "pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len)); + if (pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + break; + + default: { + struct protent *protp; + int i; + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + PPPDEBUG((LOG_INFO, "pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len)); + nb = pppSingleBuf(nb); + (*protp->input)(pd, nb->payload, nb->len); + goto out; + } + } + + /* No handler for this protocol so reject the packet. */ + PPPDEBUG((LOG_INFO, "pppInput[%d]: rejecting unsupported proto 0x%04X len=%d\n", pd, protocol, nb->len)); + if (pbuf_header(nb, sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } +#if BYTE_ORDER == LITTLE_ENDIAN + protocol = htons(protocol); + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + lcp_sprotrej(pd, nb->payload, nb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + +out: + pbuf_free(nb); + return; +} + +#if PPPOS_SUPPORT +/* + * Drop the input packet. + */ +static void +pppDrop(PPPControl *pc) +{ + if (pc->inHead != NULL) { +#if 0 + PPPDEBUG((LOG_INFO, "pppDrop: %d:%.*H\n", pc->inHead->len, min(60, pc->inHead->len * 2), pc->inHead->payload)); +#endif + PPPDEBUG((LOG_INFO, "pppDrop: pbuf len=%d\n", pc->inHead->len)); + if (pc->inTail && (pc->inTail != pc->inHead)) { + pbuf_free(pc->inTail); + } + pbuf_free(pc->inHead); + pc->inHead = NULL; + pc->inTail = NULL; + } +#if VJ_SUPPORT + vj_uncompress_err(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); +} + +/** + * Process a received octet string. + */ +static void +pppInProc(int pd, u_char *s, int l) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *nextNBuf; + u_char curChar; + + PPPDEBUG((LOG_DEBUG, "pppInProc[%d]: got %d bytes\n", pd, l)); + while (l-- > 0) { + curChar = *s++; + + /* Handle special characters. */ + if (ESCAPE_P(pc->inACCM, curChar)) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (curChar == PPP_ESCAPE) { + pc->inEscaped = 1; + /* Check for the flag character. */ + } else if (curChar == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pc->inState <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pc->inState < PDDATA) { + PPPDEBUG((LOG_WARNING, + "pppInProc[%d]: Dropping incomplete packet %d\n", + pd, pc->inState)); + LINK_STATS_INC(link.lenerr); + pppDrop(pc); + /* If the fcs is invalid, drop the packet. */ + } else if (pc->inFCS != PPP_GOODFCS) { + PPPDEBUG((LOG_INFO, + "pppInProc[%d]: Dropping bad fcs 0x%04X proto=0x%04X\n", + pd, pc->inFCS, pc->inProtocol)); + LINK_STATS_INC(link.chkerr); + pppDrop(pc); + /* Otherwise it's a good packet so pass it on. */ + } else { + /* Trim off the checksum. */ + if(pc->inTail->len >= 2) { + pc->inTail->len -= 2; + + pc->inTail->tot_len = pc->inTail->len; + if (pc->inTail != pc->inHead) { + pbuf_cat(pc->inHead, pc->inTail); + } + } else { + pc->inTail->tot_len = pc->inTail->len; + if (pc->inTail != pc->inHead) { + pbuf_cat(pc->inHead, pc->inTail); + } + + pbuf_realloc(pc->inHead, pc->inHead->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + if(tcpip_callback(pppInput, pc->inHead) != ERR_OK) { + PPPDEBUG((LOG_ERR, "pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pd)); + pbuf_free(pc->inHead); + LINK_STATS_INC(link.drop); + } + pc->inHead = NULL; + pc->inTail = NULL; + } + + /* Prepare for a new packet. */ + pc->inFCS = PPP_INITFCS; + pc->inState = PDADDRESS; + pc->inEscaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG((LOG_WARNING, + "pppInProc[%d]: Dropping ACCM char <%d>\n", pd, curChar)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pc->inEscaped) { + pc->inEscaped = 0; + curChar ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pc->inState) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (curChar != PPP_ALLSTATIONS) { + break; + } + + /* Fall through */ + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pc->inFCS = PPP_INITFCS; + + /* Fall through */ + case PDADDRESS: /* Process address field. */ + if (curChar == PPP_ALLSTATIONS) { + pc->inState = PDCONTROL; + break; + } + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (curChar == PPP_UI) { + pc->inState = PDPROTOCOL1; + break; + } +#if 0 + else { + PPPDEBUG((LOG_WARNING, + "pppInProc[%d]: Invalid control <%d>\n", pd, curChar)); + pc->inState = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (curChar & 1) { + pc->inProtocol = curChar; + pc->inState = PDDATA; + } else { + pc->inProtocol = (u_int)curChar << 8; + pc->inState = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pc->inProtocol |= curChar; + pc->inState = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pc->inTail == NULL || pc->inTail->len == PBUF_POOL_BUFSIZE) { + if(pc->inTail) { + pc->inTail->tot_len = pc->inTail->len; + if (pc->inTail != pc->inHead) { + pbuf_cat(pc->inHead, pc->inTail); + } + } + /* If we haven't started a packet, we need a packet header. */ + nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nextNBuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG((LOG_ERR, "pppInProc[%d]: NO FREE MBUFS!\n", pd)); + LINK_STATS_INC(link.memerr); + pppDrop(pc); + pc->inState = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pc->inHead == NULL) { + struct pppInputHeader *pih = nextNBuf->payload; + + pih->unit = pd; + pih->proto = pc->inProtocol; + + nextNBuf->len += sizeof(*pih); + + pc->inHead = nextNBuf; + } + pc->inTail = nextNBuf; + } + /* Load character into buffer. */ + ((u_char*)pc->inTail->payload)[pc->inTail->len++] = curChar; + break; + } + + /* update the frame check sequence number. */ + pc->inFCS = PPP_FCS(pc->inFCS, curChar); + } + } + + avRandomize(); +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +void +pppInProcOverEthernet(int pd, struct pbuf *pb) +{ + struct pppInputHeader *pih; + u16_t inProtocol; + + if(pb->len < sizeof(inProtocol)) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet: too small for protocol field\n")); + goto drop; + } + + inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for pppInputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet: could not allocate room for header\n")); + goto drop; + } + + pih = pb->payload; + + pih->unit = pd; + pih->proto = inProtocol; + + /* Dispatch the packet thereby consuming it. */ + if(tcpip_callback(pppInput, pb) != ERR_OK) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet[%d]: tcpip_callback() failed, dropping packet\n", pd)); + goto drop; + } + + return; + +drop: + LINK_STATS_INC(link.drop); + pbuf_free(pb); + return; +} +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/ppp.h b/lab/net/lwip/netif/ppp/ppp.h new file mode 100644 index 0000000..d5caa0a --- /dev/null +++ b/lab/net/lwip/netif/ppp/ppp.h @@ -0,0 +1,465 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/api.h" +#include "lwip/sockets.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/tcpip.h" +#include "lwip/netif.h" + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define TIMEOUT(f, a, t) sys_untimeout((f), (a)), sys_timeout((t)*1000, (f), (a)) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + + +#ifndef __u_char_defined + +/* Type definitions for BSD code. */ +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; + +#endif + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; ppp_trace(LOG_INFO, "Remote message: %s\n", m); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM -1 /* Invalid parameter. */ +#define PPPERR_OPEN -2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC -4 /* Unable to allocate resources. */ +#define PPPERR_USER -5 /* User interrupt. */ +#define PPPERR_CONNECT -6 /* Connection lost. */ +#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if 0 + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u_char *pkt, int len); +#endif + int enabled_flag; /* 0 iff protocol is disabled */ + char *name; /* Text name of protocol */ +#if 0 + /* Check requested options, assign defaults */ + void (*check_options) (u_long); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u_short xmit_idle; /* seconds since last NP packet sent */ + u_short recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + + u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u_int auth_required : 1; /* Peer is required to authenticate */ + u_int explicit_remote : 1; /* remote_name specified with remotename opt */ + u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u_int usehostname : 1; /* Use hostname for our_name */ + u_int usepeerdns : 1; /* Ask peer for DNS adds */ + + u_short idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +struct ppp_addrs { + struct ip_addr our_ipaddr, his_ipaddr, netmask, dns1, dns2; +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u_char *outpacket_buf[NUM_PPP]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* Initialize the PPP subsystem. */ +err_t pppInit(void); + +/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +enum pppAuthType { + PPPAUTHTYPE_NONE, + PPPAUTHTYPE_ANY, + PPPAUTHTYPE_PAP, + PPPAUTHTYPE_CHAP +}; + +void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); + +/* + * Open a new PPP connection using the given serial I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + */ +int pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); + +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); + +/* for source code compatibility */ +#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls) + +/* + * Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int pppClose(int pd); + +/* + * Indicate to the PPP process that the line has disconnected. + */ +void pppSigHUP(int pd); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int pppIOCtl(int pd, int cmd, void *arg); + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_int pppMTU(int pd); + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int pppWrite(int pd, const u_char *s, int n); + +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + +void pppMainWakeup(int pd); + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, int, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, int, int); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum NPmode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_H */ diff --git a/lab/net/lwip/netif/ppp/ppp_oe.c b/lab/net/lwip/netif/ppp/ppp_oe.c new file mode 100644 index 0000000..148770e --- /dev/null +++ b/lab/net/lwip/netif/ppp/ppp_oe.c @@ -0,0 +1,1227 @@ +/***************************************************************************** +* ppp_oe.c - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "lwip/sys.h" + +#include "netif/ppp_oe.h" +#include "netif/etharp.h" + +#include +#include + +/** @todo Replace this part with a simple list like other lwIP lists */ +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ + + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (VAL) / 256; \ + *(PTR)++ = (VAL) % 256 + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +struct pppoe_softc { + LIST_ENTRY(pppoe_softc) sc_list; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ + u8_t *sc_ac_cookie; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +static LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list; + +int pppoe_hdrlen; + +void +pppoe_init(void) +{ + pppoe_hdrlen = sizeof(struct eth_hdr) + PPPOE_HEADERLEN; + LIST_INIT(&pppoe_softc_list); +} + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = mem_malloc(sizeof(struct pppoe_softc)); + if(!sc) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list); + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc * sc; + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif == ifp) { + break; + } + } + + if(!(sc && (sc->sc_ethif == ifp))) { + return ERR_IF; + } + + tcpip_untimeout(pppoe_timeout, sc); + LIST_REMOVE(sc, sc_list); + + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + } + mem_free(sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u_int session, struct netif *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) { + return sc; + } else { + return NULL; + } + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (LIST_EMPTY(&pppoe_softc_list)) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG((LOG_DEBUG, "pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state); + return NULL; + } + if (sc->sc_ethif != rcvif) { + printf("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(void *arg) +{ + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + char *error; + u8_t *ac_cookie; + size_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off = 0, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off += sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off <= PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + error = NULL; + if (errortag && len) { + error = mem_malloc(len+1); + if (error) { + strncpy(error, (char*)pb->payload + off + sizeof(pt), len); + error[len-1] = '\0'; + } + } + if (error) { + printf("%s: %s: %s\n", devname, err_msg, error); + mem_free(error); + } else { + printf("%s: %s\n", devname, err_msg); + } + if (errortag) { + goto done; + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* printf("pppoe: free passive interface is not found\n"); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADR but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADO but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (ac_cookie) { + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + } + sc->sc_ac_cookie = mem_malloc(ac_cookie_len); + if (sc->sc_ac_cookie == NULL) { + goto done; + } + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + tcpip_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + tcpip_untimeout(pppoe_timeout, sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%04x) session = 0x%04x\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + ph->code, session); + } else { + printf("pppoe: unknown code (0x%04x) session = 0x%04x\n", ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + pppoe_dispatch_disc_pkt(netif, p); + } else { + pbuf_free(p); + } +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG((LOG_DEBUG, "pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + pppInProcOverEthernet(sc->sc_pd, pb); + + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len, l1 = 0, l2 = 0; /* XXX: gcc */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG((LOG_ERR, "ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ + if (sc->sc_service_name != NULL) { + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + int retry_wait, err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + tcpip_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + tcpip_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) { + err = EBUSY; + } else { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + sc->sc_state = PPPOE_STATE_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = htons(ETHTYPE_PPPOEDISC); + MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload + sizeof(struct eth_hdr); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + printf("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + tcpip_untimeout(pppoe_timeout, sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT */ + diff --git a/lab/net/lwip/netif/ppp/pppdebug.h b/lab/net/lwip/netif/ppp/pppdebug.h new file mode 100644 index 0000000..6253863 --- /dev/null +++ b/lab/net/lwip/netif/ppp/pppdebug.h @@ -0,0 +1,86 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/************************ +*** PUBLIC DATA TYPES *** +************************/ +/* Trace levels. */ +typedef enum { +LOG_CRITICAL = 0, +LOG_ERR = 1, +LOG_NOTICE = 2, +LOG_WARNING = 3, +LOG_INFO = 5, +LOG_DETAIL = 6, +LOG_DEBUG = 7 +} LogCodes; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * ppp_trace - a form of printf to send tracing information to stderr + */ +void ppp_trace(int level, const char *format,...); + +#define TRACELCP PPP_DEBUG + +#if PPP_DEBUG + +#define AUTHDEBUG(a) ppp_trace a +#define IPCPDEBUG(a) ppp_trace a +#define UPAPDEBUG(a) ppp_trace a +#define LCPDEBUG(a) ppp_trace a +#define FSMDEBUG(a) ppp_trace a +#define CHAPDEBUG(a) ppp_trace a +#define PPPDEBUG(a) ppp_trace a + +#else /* PPP_DEBUG */ + +#define AUTHDEBUG(a) +#define IPCPDEBUG(a) +#define UPAPDEBUG(a) +#define LCPDEBUG(a) +#define FSMDEBUG(a) +#define CHAPDEBUG(a) +#define PPPDEBUG(a) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ diff --git a/lab/net/lwip/netif/ppp/randm.c b/lab/net/lwip/netif/ppp/randm.c new file mode 100644 index 0000000..0c622a0 --- /dev/null +++ b/lab/net/lwip/netif/ppp/randm.c @@ -0,0 +1,248 @@ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "md5.h" +#include "randm.h" + +#include "ppp.h" +#include "pppdebug.h" + + +#if MD5_SUPPORT /* this module depends on MD5 */ +#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static char randPool[RANDPOOLSZ]; /* Pool of randomness. */ +static long randCount = 0; /* Pseudo-random incrementer */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Since this is to be called on power up, we don't have much + * system randomess to work with. Here all we use is the + * real-time clock. We'll accumulate more randomness as soon + * as things start happening. + */ +void +avRandomInit() +{ + avChurnRand(NULL, 0); +} + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +void +avChurnRand(char *randData, u32_t randLen) +{ + MD5_CTX md5; + + /* ppp_trace(LOG_INFO, "churnRand: %u@%P\n", randLen, randData); */ + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + if (randData) { + MD5Update(&md5, (u_char *)randData, randLen); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + char foobar; + } sysData; + + /* Load sysData fields here. */ + MD5Update(&md5, (u_char *)&sysData, sizeof(sysData)); + } + MD5Final((u_char *)randPool, &md5); +/* ppp_trace(LOG_INFO, "churnRand: -> 0\n"); */ +} + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Note: It's important that there be sufficient randomness in randPool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call churnRand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * randCount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void +avGenRand(char *buf, u32_t bufLen) +{ + MD5_CTX md5; + u_char tmp[16]; + u32_t n; + + while (bufLen > 0) { + n = LWIP_MIN(bufLen, RANDPOOLSZ); + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + MD5Update(&md5, (u_char *)&randCount, sizeof(randCount)); + MD5Final(tmp, &md5); + randCount++; + MEMCPY(buf, tmp, n); + buf += n; + bufLen -= n; + } +} + +/* + * Return a new random number. + */ +u32_t +avRandom() +{ + u32_t newRand; + + avGenRand((char *)&newRand, sizeof(newRand)); + + return newRand; +} + +#else /* MD5_SUPPORT */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static int avRandomized = 0; /* Set when truely randomized. */ +static u32_t avRandomSeed = 0; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void +avRandomInit() +{ +#if 0 + /* Get a pointer into the last 4 bytes of clockBuf. */ + u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]); + + /* + * Initialize our seed using the real-time clock, the idle + * counter, the millisecond timer, and the hardware timer + * tick counter. The real-time clock and the hardware + * tick counter are the best sources of randomness but + * since the tick counter is only 16 bit (and truncated + * at that), the idle counter and millisecond timer + * (which may be small values) are added to help + * randomize the lower 16 bits of the seed. + */ + readClk(); + avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr + + ppp_mtime() + ((u32_t)TM1 << 16) + TM1; +#else + avRandomSeed += sys_jiffies(); /* XXX */ +#endif + + /* Initialize the Borland random number generator. */ + srand((unsigned)avRandomSeed); +} + +/* + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void +avRandomize(void) +{ + static u32_t last_jiffies; + + if (!avRandomized) { + avRandomized = !0; + avRandomInit(); + /* The initialization function also updates the seed. */ + } else { + /* avRandomSeed += (avRandomSeed << 16) + TM1; */ + avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = sys_jiffies(); +} + +/* + * Return a new random number. + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t +avRandom() +{ + return ((((u32_t)rand() << 16) + rand()) + avRandomSeed); +} + +#endif /* MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/randm.h b/lab/net/lwip/netif/ppp/randm.h new file mode 100644 index 0000000..a0984b0 --- /dev/null +++ b/lab/net/lwip/netif/ppp/randm.h @@ -0,0 +1,81 @@ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#ifndef RANDM_H +#define RANDM_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * Initialize the random number generator. + */ +void avRandomInit(void); + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + */ +void avChurnRand(char *randData, u32_t randLen); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +#if MD5_SUPPORT +#define avRandomize() avChurnRand(NULL, 0) +#else /* MD5_SUPPORT */ +void avRandomize(void); +#endif /* MD5_SUPPORT */ + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void avGenRand(char *buf, u32_t bufLen); + +/* + * Return a new random number. + */ +u32_t avRandom(void); + + +#endif /* RANDM_H */ diff --git a/lab/net/lwip/netif/ppp/vj.c b/lab/net/lwip/netif/ppp/vj.c new file mode 100644 index 0000000..814ea72 --- /dev/null +++ b/lab/net/lwip/netif/ppp/vj.c @@ -0,0 +1,660 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "vj.h" + +#include + +#if VJ_SUPPORT + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +#if defined(NO_CHAR_BITFIELDS) +#define getip_hl(base) ((base).ip_hl_v&0xf) +#define getth_off(base) (((base).th_x2_off&0xf0)>>4) +#else +#define getip_hl(base) ((base).ip_hl) +#define getth_off(base) ((base).th_off) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + register u_int i; + register struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp); \ + cp += 3; \ + } else { \ + u32_t tmp = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp); \ + cp += 3; \ + } else { \ + u_short tmp = ntohs(f) + (u_short)*cp++; \ + (f) = htons(tmp); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_short)*cp++); \ + } \ +} + +/* + * vj_compress_tcp - Attempt to do Van Jacobsen header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u_int +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) +{ + register struct ip *ip = (struct ip *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u_short hlen = getip_hl(*ip); + register struct tcphdr *oth; + register struct tcphdr *th; + register u_short deltaS, deltaA; + register u_long deltaL; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (ip->ip_p != IPPROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((ip->ip_off & htons(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcphdr *)&((long *)ip)[hlen]; + if ((th->th_flags & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr + || ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr + || *(long *)th != ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr + && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr + && *(long *)th == ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += getth_off(*th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcphdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += getth_off(*th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG((LOG_INFO, "vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] + || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] + || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] + || getth_off(*th) != getth_off(*oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (th->th_flags & TCP_URG) { + deltaS = ntohs(th->th_urp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->th_urp != oth->th_urp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->th_ack) - ntohl(oth->th_ack)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u_short)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->th_seq) - ntohl(oth->th_seq)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u_short)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (ip->ip_len != cs->cs_ip.ip_len && + ntohs(cs->cs_ip.ip_len) == hlen) { + break; + } + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = (u_short)(ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id)); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (th->th_flags & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->th_sum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u_short)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = changes | NEW_C; + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = changes; + } + *cp++ = deltaA >> 8; + *cp++ = deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + BCOPY(ip, &cs->cs_ip, hlen); + ip->ip_p = cs->cs_id; + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + register u_int hlen; + register struct cstate *cs; + register struct ip *ip; + + ip = (struct ip *)nb->payload; + hlen = getip_hl(*ip) << 2; + if (ip->ip_p >= MAX_SLOTS + || hlen + sizeof(struct tcphdr) > nb->len + || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG((LOG_INFO, "vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + ip->ip_p, hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = ip->ip_p]; + comp->flags &=~ VJF_TOSS; + ip->ip_p = IPPROTO_TCP; + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u_char *cp; + struct tcphdr *th; + struct cstate *cs; + u_short *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u_int vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u_char *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = getip_hl(cs->cs_ip) << 2; + th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->th_sum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + th->th_flags |= TCP_PSH; + } else { + th->th_flags &=~ TCP_PSH; + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->th_ack) + i; + th->th_ack = htonl(tmp); + tmp = ntohl(th->th_seq) + i; + th->th_seq = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + th->th_seq = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + th->th_flags |= TCP_URG; + DECODEU(th->th_urp); + } else { + th->th_flags &=~ TCP_URG; + } + if (changes & NEW_W) { + DECODES(th->th_win); + } + if (changes & NEW_A) { + DECODEL(th->th_ack); + } + if (changes & NEW_S) { + DECODEL(th->th_seq); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip.ip_id); + } else { + cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1; + cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u_short)(cp - (u_char*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + cs->cs_ip.ip_len = htons(tmp); +#else + cs->cs_ip.ip_len = htons(n0->tot_len - vjlen + cs->cs_hlen); +#endif + + /* recompute the ip header checksum */ + bp = (u_short *) &cs->cs_ip; + cs->cs_ip.ip_sum = 0; + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + cs->cs_ip.ip_sum = (u_short)(~tmp); + + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* VJ_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/lab/net/lwip/netif/ppp/vj.h b/lab/net/lwip/netif/ppp/vj.h new file mode 100644 index 0000000..b9617da --- /dev/null +++ b/lab/net/lwip/netif/ppp/vj.h @@ -0,0 +1,155 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#ifndef VJ_H +#define VJ_H + +#include "vjbsdhdr.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; + u_char maxSlotIndex; + u_char compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ diff --git a/lab/net/lwip/netif/ppp/vjbsdhdr.h b/lab/net/lwip/netif/ppp/vjbsdhdr.h new file mode 100644 index 0000000..f462676 --- /dev/null +++ b/lab/net/lwip/netif/ppp/vjbsdhdr.h @@ -0,0 +1,75 @@ +#ifndef VJBSDHDR_H +#define VJBSDHDR_H + +#include "lwip/tcp.h" + +/* + * Structure of an internet header, naked of options. + * + * We declare ip_len and ip_off to be short, rather than u_short + * pragmatically since otherwise unsigned comparisons can result + * against negative integers quite easily, and fail in subtle ways. + */ +PACK_STRUCT_BEGIN +struct ip +{ +#if defined(NO_CHAR_BITFIELDS) + u_char ip_hl_v; /* bug in GCC for mips means the bitfield stuff will sometimes break - so we use a char for both and get round it with macro's instead... */ +#else +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned ip_hl:4, /* header length */ + ip_v :4; /* version */ +#elif BYTE_ORDER == BIG_ENDIAN + unsigned ip_v :4, /* version */ + ip_hl:4; /* header length */ +#else + COMPLAIN - NO BYTE ORDER SELECTED! +#endif +#endif + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +}; +PACK_STRUCT_END + +typedef u32_t tcp_seq; + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +PACK_STRUCT_BEGIN +struct tcphdr +{ + u_short th_sport; /* source port */ + u_short th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +#if defined(NO_CHAR_BITFIELDS) + u_char th_x2_off; +#else +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned th_x2 :4, /* (unused) */ + th_off:4; /* data offset */ +#endif +#if BYTE_ORDER == BIG_ENDIAN + unsigned th_off:4, /* data offset */ + th_x2 :4; /* (unused) */ +#endif +#endif + u_char th_flags; + u_short th_win; /* window */ + u_short th_sum; /* checksum */ + u_short th_urp; /* urgent pointer */ +}; +PACK_STRUCT_END + +#endif /* VJBSDHDR_H */ diff --git a/lab/net/lwip/netif/slipif.c b/lab/net/lwip/netif/slipif.c new file mode 100644 index 0000000..6cb2db4 --- /dev/null +++ b/lab/net/lwip/netif/slipif.c @@ -0,0 +1,279 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_recv and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sio.h" + +#define SLIP_END 0300 /* 0xC0 */ +#define SLIP_ESC 0333 /* 0xDB */ +#define SLIP_ESC_END 0334 /* 0xDC */ +#define SLIP_ESC_ESC 0335 /* 0xDD */ + +#define MAX_SIZE 1500 + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +err_t +slipif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) +{ + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_UNUSED_ARG(ipaddr); + + /* Send pbuf out on the serial I/O device. */ + sio_send(SLIP_END, netif->state); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + sio_send(SLIP_ESC, netif->state); + sio_send(SLIP_ESC_END, netif->state); + break; + case SLIP_ESC: + sio_send(SLIP_ESC, netif->state); + sio_send(SLIP_ESC_ESC, netif->state); + break; + default: + sio_send(c, netif->state); + break; + } + } + } + sio_send(SLIP_END, netif->state); + return ERR_OK; +} + +/** + * Handle the incoming SLIP stream character by character + * + * Poll the serial layer by calling sio_recv() + * + * @param netif the lwip network interface structure for this slipif + * @return The IP packet when SLIP_END is received + */ +static struct pbuf * +slipif_input(struct netif *netif) +{ + u8_t c; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + u16_t recved; + u16_t i; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + q = p = NULL; + recved = i = 0; + c = 0; + + while (1) { + c = sio_recv(netif->state); + switch (c) { + case SLIP_END: + if (recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(q, recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n")); + return q; + } + break; + + case SLIP_ESC: + c = sio_recv(netif->state); + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + /* FALLTHROUGH */ + + default: + /* byte received, packet not yet completely received */ + if (p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + p = pbuf_alloc(PBUF_LINK, PBUF_POOL_BUFSIZE, PBUF_POOL); + + if (p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + break; + } + + if (q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(q, p); + } else { + /* p is the first pbuf in the chain */ + q = p; + } + } + + /* this automatically drops bytes if > MAX_SIZE */ + if ((p != NULL) && (recved <= MAX_SIZE)) { + ((u8_t *)p->payload)[i] = c; + recved++; + i++; + if (i >= p->len) { + /* on to the next pbuf */ + i = 0; + if (p->next != NULL && p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + p = p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + p = NULL; + } + } + } + break; + } + } + return NULL; +} + +#if !NO_SYS +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop(void *nf) +{ + struct pbuf *p; + struct netif *netif = (struct netif *)nf; + + while (1) { + p = slipif_input(netif); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } +} +#endif /* !NO_SYS */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default) + */ +err_t +slipif_init(struct netif *netif) +{ + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output; + netif->mtu = MAX_SIZE; + netif->flags = NETIF_FLAG_POINTTOPOINT; + + /* Try to open the serial port (netif->num contains the port number). */ + netif->state = sio_open(netif->num); + if (!netif->state) { + /* Opening the serial port failed. */ + return ERR_IF; + } + + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made without knowing more about the + * serial line! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, 0); + + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop, netif, SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); + return ERR_OK; +} +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/lab/net/ns.h b/lab/net/ns.h new file mode 100644 index 0000000..392abf8 --- /dev/null +++ b/lab/net/ns.h @@ -0,0 +1,22 @@ +#include +#include + +#define IP "10.0.2.15" +#define MASK "255.255.255.0" +#define DEFAULT "10.0.2.2" + +#define TIMER_INTERVAL 250 + +// Virtual address at which to receive page mappings containing client requests. +#define QUEUE_SIZE 20 +#define REQVA (0x0ffff000 - QUEUE_SIZE * PGSIZE) + +/* timer.c */ +void timer(envid_t ns_envid, uint32_t initial_to); + +/* input.c */ +void input(envid_t ns_envid); + +/* output.c */ +void output(envid_t ns_envid); + diff --git a/lab/net/output.c b/lab/net/output.c new file mode 100644 index 0000000..b20769e --- /dev/null +++ b/lab/net/output.c @@ -0,0 +1,27 @@ +#include "ns.h" + +extern union Nsipc nsipcbuf; + +void +output(envid_t ns_envid) +{ + binaryname = "ns_output"; + + // LAB 6: Your code here: + // - read a packet from the network server + // - send the packet to the device driver + uint32_t whom; + int perm; + int32_t req; + + while (1) { + req = ipc_recv((envid_t *)&whom, &nsipcbuf, &perm); + if (req != NSREQ_OUTPUT) { + continue; + } + + while (sys_pkt_try_send(nsipcbuf.pkt.jp_data, nsipcbuf.pkt.jp_len) < 0) { + sys_yield(); + } + } +} diff --git a/lab/net/serv.c b/lab/net/serv.c new file mode 100644 index 0000000..102d96d --- /dev/null +++ b/lab/net/serv.c @@ -0,0 +1,356 @@ +/* + * Network server main loop - + * serves IPC requests from other environments. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ns.h" + +/* errno to make lwIP happy */ +int errno; + +struct netif nif; + +#define debug 0 + +struct timer_thread { + uint32_t msec; + void (*func)(void); + const char *name; +}; + +static struct timer_thread t_arp; +static struct timer_thread t_tcpf; +static struct timer_thread t_tcps; + +static envid_t timer_envid; +static envid_t input_envid; +static envid_t output_envid; + +static bool buse[QUEUE_SIZE]; +static int next_i(int i) { return (i+1) % QUEUE_SIZE; } +static int prev_i(int i) { return (i ? i-1 : QUEUE_SIZE-1); } + +static void * +get_buffer(void) { + void *va; + + int i; + for (i = 0; i < QUEUE_SIZE; i++) + if (!buse[i]) break; + + if (i == QUEUE_SIZE) { + panic("NS: buffer overflow"); + return 0; + } + + va = (void *)(REQVA + i * PGSIZE); + buse[i] = 1; + + return va; +} + +static void +put_buffer(void *va) { + int i = ((uint32_t)va - REQVA) / PGSIZE; + buse[i] = 0; +} + +static void +lwip_init(struct netif *nif, void *if_state, + uint32_t init_addr, uint32_t init_mask, uint32_t init_gw) +{ + struct ip_addr ipaddr, netmask, gateway; + ipaddr.addr = init_addr; + netmask.addr = init_mask; + gateway.addr = init_gw; + + if (0 == netif_add(nif, &ipaddr, &netmask, &gateway, + if_state, + jif_init, + ip_input)) + panic("lwip_init: error in netif_add\n"); + + netif_set_default(nif); + netif_set_up(nif); +} + +static void __attribute__((noreturn)) +net_timer(uint32_t arg) +{ + struct timer_thread *t = (struct timer_thread *) arg; + + for (;;) { + uint32_t cur = sys_time_msec(); + + lwip_core_lock(); + t->func(); + lwip_core_unlock(); + + thread_wait(0, 0, cur + t->msec); + } +} + +static void +start_timer(struct timer_thread *t, void (*func)(void), const char *name, int msec) +{ + t->msec = msec; + t->func = func; + t->name = name; + int r = thread_create(0, name, &net_timer, (uint32_t)t); + if (r < 0) + panic("cannot create timer thread: %s", e2s(r)); +} + +static void +tcpip_init_done(void *arg) +{ + uint32_t *done = arg; + *done = 1; + thread_wakeup(done); +} + +void +serve_init(uint32_t ipaddr, uint32_t netmask, uint32_t gw) +{ + int r; + lwip_core_lock(); + + uint32_t done = 0; + tcpip_init(&tcpip_init_done, &done); + lwip_core_unlock(); + thread_wait(&done, 0, (uint32_t)~0); + lwip_core_lock(); + + lwip_init(&nif, &output_envid, ipaddr, netmask, gw); + + start_timer(&t_arp, ðarp_tmr, "arp timer", ARP_TMR_INTERVAL); + start_timer(&t_tcpf, &tcp_fasttmr, "tcp f timer", TCP_FAST_INTERVAL); + start_timer(&t_tcps, &tcp_slowtmr, "tcp s timer", TCP_SLOW_INTERVAL); + + struct in_addr ia = {ipaddr}; + cprintf("ns: %02x:%02x:%02x:%02x:%02x:%02x" + " bound to static IP %s\n", + nif.hwaddr[0], nif.hwaddr[1], nif.hwaddr[2], + nif.hwaddr[3], nif.hwaddr[4], nif.hwaddr[5], + inet_ntoa(ia)); + + lwip_core_unlock(); + + cprintf("NS: TCP/IP initialized.\n"); +} + +static void +process_timer(envid_t envid) { + uint32_t start, now, to; + + if (envid != timer_envid) { + cprintf("NS: received timer interrupt from envid %x not timer env\n", envid); + return; + } + + start = sys_time_msec(); + thread_yield(); + now = sys_time_msec(); + + to = TIMER_INTERVAL - (now - start); + ipc_send(envid, to, 0, 0); +} + +struct st_args { + int32_t reqno; + uint32_t whom; + union Nsipc *req; +}; + +static void +serve_thread(uint32_t a) { + struct st_args *args = (struct st_args *)a; + union Nsipc *req = args->req; + int r; + + switch (args->reqno) { + case NSREQ_ACCEPT: + { + struct Nsret_accept ret; + ret.ret_addrlen = req->accept.req_addrlen; + r = lwip_accept(req->accept.req_s, &ret.ret_addr, + &ret.ret_addrlen); + memmove(req, &ret, sizeof ret); + break; + } + case NSREQ_BIND: + r = lwip_bind(req->bind.req_s, &req->bind.req_name, + req->bind.req_namelen); + break; + case NSREQ_SHUTDOWN: + r = lwip_shutdown(req->shutdown.req_s, req->shutdown.req_how); + break; + case NSREQ_CLOSE: + r = lwip_close(req->close.req_s); + break; + case NSREQ_CONNECT: + r = lwip_connect(req->connect.req_s, &req->connect.req_name, + req->connect.req_namelen); + break; + case NSREQ_LISTEN: + r = lwip_listen(req->listen.req_s, req->listen.req_backlog); + break; + case NSREQ_RECV: + // Note that we read the request fields before we + // overwrite it with the response data. + r = lwip_recv(req->recv.req_s, req->recvRet.ret_buf, + req->recv.req_len, req->recv.req_flags); + break; + case NSREQ_SEND: + r = lwip_send(req->send.req_s, &req->send.req_buf, + req->send.req_size, req->send.req_flags); + break; + case NSREQ_SOCKET: + r = lwip_socket(req->socket.req_domain, req->socket.req_type, + req->socket.req_protocol); + break; + case NSREQ_INPUT: + jif_input(&nif, (void *)&req->pkt); + r = 0; + break; + default: + cprintf("Invalid request code %d from %08x\n", args->whom, args->req); + r = -E_INVAL; + break; + } + + if (r == -1) { + char buf[100]; + snprintf(buf, sizeof buf, "ns req type %d", args->reqno); + perror(buf); + } + + if (args->reqno != NSREQ_INPUT) + ipc_send(args->whom, r, 0, 0); + + put_buffer(args->req); + sys_page_unmap(0, (void*) args->req); + free(args); +} + +void +serve(void) { + int32_t reqno; + uint32_t whom; + int i, perm; + void *va; + + while (1) { + // ipc_recv will block the entire process, so we flush + // all pending work from other threads. We limit the + // number of yields in case there's a rogue thread. + for (i = 0; thread_wakeups_pending() && i < 32; ++i) + thread_yield(); + + perm = 0; + va = get_buffer(); + reqno = ipc_recv((int32_t *) &whom, (void *) va, &perm); + if (debug) { + cprintf("ns req %d from %08x\n", reqno, whom); + } + + // first take care of requests that do not contain an argument page + if (reqno == NSREQ_TIMER) { + process_timer(whom); + put_buffer(va); + continue; + } + + // All remaining requests must contain an argument page + if (!(perm & PTE_P)) { + cprintf("Invalid request from %08x: no argument page\n", whom); + continue; // just leave it hanging... + } + + // Since some lwIP socket calls will block, create a thread and + // process the rest of the request in the thread. + struct st_args *args = malloc(sizeof(struct st_args)); + if (!args) + panic("could not allocate thread args structure"); + + args->reqno = reqno; + args->whom = whom; + args->req = va; + + thread_create(0, "serve_thread", serve_thread, (uint32_t)args); + thread_yield(); // let the thread created run + } +} + +static void +tmain(uint32_t arg) { + serve_init(inet_addr(IP), + inet_addr(MASK), + inet_addr(DEFAULT)); + serve(); +} + +void +umain(int argc, char **argv) +{ + envid_t ns_envid = sys_getenvid(); + + binaryname = "ns"; + + // fork off the timer thread which will send us periodic messages + timer_envid = fork(); + if (timer_envid < 0) + panic("error forking"); + else if (timer_envid == 0) { + timer(ns_envid, TIMER_INTERVAL); + return; + } + + // fork off the input thread which will poll the NIC driver for input + // packets + input_envid = fork(); + if (input_envid < 0) + panic("error forking"); + else if (input_envid == 0) { + input(ns_envid); + return; + } + + // fork off the output thread that will send the packets to the NIC + // driver + output_envid = fork(); + if (output_envid < 0) + panic("error forking"); + else if (output_envid == 0) { + output(ns_envid); + return; + } + + // lwIP requires a user threading library; start the library and jump + // into a thread to continue initialization. + thread_init(); + thread_create(0, "main", tmain, 0); + thread_yield(); + // never coming here! +} diff --git a/lab/net/testinput.c b/lab/net/testinput.c new file mode 100644 index 0000000..22cf937 --- /dev/null +++ b/lab/net/testinput.c @@ -0,0 +1,115 @@ +#include "ns.h" +#include + +static envid_t output_envid; +static envid_t input_envid; + +static struct jif_pkt *pkt = (struct jif_pkt*)REQVA; + + +static void +announce(void) +{ + // We need to pre-announce our IP so we don't have to deal + // with ARP requests. Ideally, we would use gratuitous ARP + // for this, but QEMU's ARP implementation is dumb and only + // listens for very specific ARP requests, such as requests + // for the gateway IP. + + uint8_t mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + uint32_t myip = inet_addr(IP); + uint32_t gwip = inet_addr(DEFAULT); + int r; + + if ((r = sys_page_alloc(0, pkt, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_map: %e", r); + + struct etharp_hdr *arp = (struct etharp_hdr*)pkt->jp_data; + pkt->jp_len = sizeof(*arp); + + memset(arp->ethhdr.dest.addr, 0xff, ETHARP_HWADDR_LEN); + memcpy(arp->ethhdr.src.addr, mac, ETHARP_HWADDR_LEN); + arp->ethhdr.type = htons(ETHTYPE_ARP); + arp->hwtype = htons(1); // Ethernet + arp->proto = htons(ETHTYPE_IP); + arp->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | 4); + arp->opcode = htons(ARP_REQUEST); + memcpy(arp->shwaddr.addr, mac, ETHARP_HWADDR_LEN); + memcpy(arp->sipaddr.addrw, &myip, 4); + memset(arp->dhwaddr.addr, 0x00, ETHARP_HWADDR_LEN); + memcpy(arp->dipaddr.addrw, &gwip, 4); + + ipc_send(output_envid, NSREQ_OUTPUT, pkt, PTE_P|PTE_W|PTE_U); + sys_page_unmap(0, pkt); +} + +static void +hexdump(const char *prefix, const void *data, int len) +{ + int i; + char buf[80]; + char *end = buf + sizeof(buf); + char *out = NULL; + for (i = 0; i < len; i++) { + if (i % 16 == 0) + out = buf + snprintf(buf, end - buf, + "%s%04x ", prefix, i); + out += snprintf(out, end - out, "%02x", ((uint8_t*)data)[i]); + if (i % 16 == 15 || i == len - 1) + cprintf("%.*s\n", out - buf, buf); + if (i % 2 == 1) + *(out++) = ' '; + if (i % 16 == 7) + *(out++) = ' '; + } +} + +void +umain(int argc, char **argv) +{ + envid_t ns_envid = sys_getenvid(); + int i, r, first = 1; + + binaryname = "testinput"; + + output_envid = fork(); + if (output_envid < 0) + panic("error forking"); + else if (output_envid == 0) { + output(ns_envid); + return; + } + + input_envid = fork(); + if (input_envid < 0) + panic("error forking"); + else if (input_envid == 0) { + input(ns_envid); + return; + } + + cprintf("Sending ARP announcement...\n"); + announce(); + + while (1) { + envid_t whom; + int perm; + + int32_t req = ipc_recv((int32_t *)&whom, pkt, &perm); + if (req < 0) + panic("ipc_recv: %e", req); + if (whom != input_envid) + panic("IPC from unexpected environment %08x", whom); + if (req != NSREQ_INPUT) + panic("Unexpected IPC %d", req); + + hexdump("input: ", pkt->jp_data, pkt->jp_len); + cprintf("\n"); + + // Only indicate that we're waiting for packets once + // we've received the ARP reply + if (first) + cprintf("Waiting for packets...\n"); + first = 0; + } +} diff --git a/lab/net/testoutput.c b/lab/net/testoutput.c new file mode 100644 index 0000000..99e87bc --- /dev/null +++ b/lab/net/testoutput.c @@ -0,0 +1,42 @@ +#include "ns.h" + +#ifndef TESTOUTPUT_COUNT +#define TESTOUTPUT_COUNT 10 +#endif + +static envid_t output_envid; + +static struct jif_pkt *pkt = (struct jif_pkt*)REQVA; + + +void +umain(int argc, char **argv) +{ + envid_t ns_envid = sys_getenvid(); + int i, r; + + binaryname = "testoutput"; + + output_envid = fork(); + if (output_envid < 0) + panic("error forking"); + else if (output_envid == 0) { + output(ns_envid); + return; + } + + for (i = 0; i < TESTOUTPUT_COUNT; i++) { + if ((r = sys_page_alloc(0, pkt, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_alloc: %e", r); + pkt->jp_len = snprintf(pkt->jp_data, + PGSIZE - sizeof(pkt->jp_len), + "Packet %02d", i); + cprintf("Transmitting packet %d\n", i); + ipc_send(output_envid, NSREQ_OUTPUT, pkt, PTE_P|PTE_W|PTE_U); + sys_page_unmap(0, pkt); + } + + // Spin for a while, just in case IPC's or packets need to be flushed + for (i = 0; i < TESTOUTPUT_COUNT*2; i++) + sys_yield(); +} diff --git a/lab/net/timer.c b/lab/net/timer.c new file mode 100644 index 0000000..9708379 --- /dev/null +++ b/lab/net/timer.c @@ -0,0 +1,32 @@ +#include "ns.h" + +void +timer(envid_t ns_envid, uint32_t initial_to) { + int r; + uint32_t stop = sys_time_msec() + initial_to; + + binaryname = "ns_timer"; + + while (1) { + while((r = sys_time_msec()) < stop && r >= 0) { + sys_yield(); + } + if (r < 0) + panic("sys_time_msec: %e", r); + + ipc_send(ns_envid, NSREQ_TIMER, 0, 0); + + while (1) { + uint32_t to, whom; + to = ipc_recv((int32_t *) &whom, 0, 0); + + if (whom != ns_envid) { + cprintf("NS TIMER: timer thread got IPC message from env %x not NS\n", whom); + continue; + } + + stop = sys_time_msec() + to; + break; + } + } +} diff --git a/lab/user/Makefrag b/lab/user/Makefrag index cae1626..f2b5c2c 100644 --- a/lab/user/Makefrag +++ b/lab/user/Makefrag @@ -1,5 +1,6 @@ OBJDIRS += user +USERLIBS += lwip USERLIBS += jos diff --git a/lab/user/echosrv.c b/lab/user/echosrv.c new file mode 100644 index 0000000..3a1d6ad --- /dev/null +++ b/lab/user/echosrv.c @@ -0,0 +1,89 @@ +#include +#include +#include + +#define PORT 7 + +#define BUFFSIZE 32 +#define MAXPENDING 5 // Max connection requests + +static void +die(char *m) +{ + cprintf("%s\n", m); + exit(); +} + +void +handle_client(int sock) +{ + char buffer[BUFFSIZE]; + int received = -1; + // Receive message + if ((received = read(sock, buffer, BUFFSIZE)) < 0) + die("Failed to receive initial bytes from client"); + + // Send bytes and check for more incoming data in loop + while (received > 0) { + // Send back received data + if (write(sock, buffer, received) != received) + die("Failed to send bytes to client"); + + // Check for more data + if ((received = read(sock, buffer, BUFFSIZE)) < 0) + die("Failed to receive additional bytes from client"); + } + close(sock); +} + +void +umain(int argc, char **argv) +{ + int serversock, clientsock; + struct sockaddr_in echoserver, echoclient; + char buffer[BUFFSIZE]; + unsigned int echolen; + int received = 0; + + // Create the TCP socket + if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + die("Failed to create socket"); + + cprintf("opened socket\n"); + + // Construct the server sockaddr_in structure + memset(&echoserver, 0, sizeof(echoserver)); // Clear struct + echoserver.sin_family = AF_INET; // Internet/IP + echoserver.sin_addr.s_addr = htonl(INADDR_ANY); // IP address + echoserver.sin_port = htons(PORT); // server port + + cprintf("trying to bind\n"); + + // Bind the server socket + if (bind(serversock, (struct sockaddr *) &echoserver, + sizeof(echoserver)) < 0) { + die("Failed to bind the server socket"); + } + + // Listen on the server socket + if (listen(serversock, MAXPENDING) < 0) + die("Failed to listen on server socket"); + + cprintf("bound\n"); + + // Run until canceled + while (1) { + unsigned int clientlen = sizeof(echoclient); + // Wait for client connection + if ((clientsock = + accept(serversock, (struct sockaddr *) &echoclient, + &clientlen)) < 0) { + die("Failed to accept client connection"); + } + cprintf("Client connected: %s\n", inet_ntoa(echoclient.sin_addr)); + handle_client(clientsock); + } + + close(serversock); + +} diff --git a/lab/user/echotest.c b/lab/user/echotest.c new file mode 100644 index 0000000..6e8c6e7 --- /dev/null +++ b/lab/user/echotest.c @@ -0,0 +1,68 @@ +#include +#include +#include + +#define BUFFSIZE 32 +#define IPADDR "10.0.2.15" +#define PORT 10000 + +const char *msg = "Hello world!\n"; + +static void +die(char *m) +{ + cprintf("%s\n", m); + exit(); +} + +void umain(int argc, char **argv) +{ + int sock; + struct sockaddr_in echoserver; + char buffer[BUFFSIZE]; + unsigned int echolen; + int received = 0; + + cprintf("Connecting to:\n"); + cprintf("\tip address %s = %x\n", IPADDR, inet_addr(IPADDR)); + + // Create the TCP socket + if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + die("Failed to create socket"); + + cprintf("opened socket\n"); + + // Construct the server sockaddr_in structure + memset(&echoserver, 0, sizeof(echoserver)); // Clear struct + echoserver.sin_family = AF_INET; // Internet/IP + echoserver.sin_addr.s_addr = inet_addr(IPADDR); // IP address + echoserver.sin_port = htons(PORT); // server port + + cprintf("trying to connect to server\n"); + + // Establish connection + if (connect(sock, (struct sockaddr *) &echoserver, sizeof(echoserver)) < 0) + die("Failed to connect with server"); + + cprintf("connected to server\n"); + + // Send the word to the server + echolen = strlen(msg); + if (write(sock, msg, echolen) != echolen) + die("Mismatch in number of sent bytes"); + + // Receive the word back from the server + cprintf("Received: \n"); + while (received < echolen) { + int bytes = 0; + if ((bytes = read(sock, buffer, BUFFSIZE-1)) < 1) { + die("Failed to receive bytes from server"); + } + received += bytes; + buffer[bytes] = '\0'; // Assure null terminated string + cprintf(buffer); + } + cprintf("\n"); + + close(sock); +} diff --git a/lab/user/faultio.c b/lab/user/faultio.c index 46b5f89..b2fdbeb 100644 --- a/lab/user/faultio.c +++ b/lab/user/faultio.c @@ -18,5 +18,7 @@ umain(int argc, char **argv) // fault, because user-level code shouldn't be able to use the io space. outb(0x1F6, 0xE0 | (1<<4)); + cprintf("%s: made it here --- bug\n"); + } diff --git a/lab/user/httpd.c b/lab/user/httpd.c new file mode 100644 index 0000000..c6eb3d1 --- /dev/null +++ b/lab/user/httpd.c @@ -0,0 +1,354 @@ +#include +#include +#include + +#define PORT 80 +#define VERSION "0.1" +#define HTTP_VERSION "1.0" + +#define E_BAD_REQ 1000 + +#define BUFFSIZE 512 +#define MAXPENDING 5 // Max connection requests + +struct http_request { + int sock; + char *url; + char *version; +}; + +struct responce_header { + int code; + char *header; +}; + +struct responce_header headers[] = { + { 200, "HTTP/" HTTP_VERSION " 200 OK\r\n" + "Server: jhttpd/" VERSION "\r\n"}, + {0, 0}, +}; + +struct error_messages { + int code; + char *msg; +}; + +struct error_messages errors[] = { + {400, "Bad Request"}, + {404, "Not Found"}, +}; + +static void +die(char *m) +{ + cprintf("%s\n", m); + exit(); +} + +static void +req_free(struct http_request *req) +{ + free(req->url); + free(req->version); +} + +static int +send_header(struct http_request *req, int code) +{ + struct responce_header *h = headers; + // 搜索相应的 header + while (h->code != 0 && h->header!= 0) { + if (h->code == code) + break; + h++; + } + + if (h->code == 0) + return -1; + + int len = strlen(h->header); + if (write(req->sock, h->header, len) != len) { + die("Failed to send bytes to client"); + } + + return 0; +} + +static int +send_data(struct http_request *req, int fd) +{ + // LAB 6: Your code here. + // panic("send_data not implemented"); + // 从fd 中读size大小数据,并发送 + int r; + size_t len; + char buf[BUFFSIZE]; + if ( (r = read(fd, buf, BUFFSIZE)) < 0 ) + return -1; + + len = r; + if ( write(req->sock, buf, len) != len) { + die("Failed to send bytes to client"); + } + + return 0; +} + +static int +send_size(struct http_request *req, off_t size) +{ + char buf[64]; + int r; + + r = snprintf(buf, 64, "Content-Length: %ld\r\n", (long)size); + if (r > 63) + panic("buffer too small!"); + + if (write(req->sock, buf, r) != r) + return -1; + + return 0; +} + +static const char* +mime_type(const char *file) +{ + //TODO: for now only a single mime type + return "text/html"; +} + +static int +send_content_type(struct http_request *req) +{ + char buf[128]; + int r; + const char *type; + + type = mime_type(req->url); + if (!type) + return -1; + + r = snprintf(buf, 128, "Content-Type: %s\r\n", type); + if (r > 127) + panic("buffer too small!"); + + if (write(req->sock, buf, r) != r) + return -1; + + return 0; +} + +static int +send_header_fin(struct http_request *req) +{ + const char *fin = "\r\n"; + int fin_len = strlen(fin); + + if (write(req->sock, fin, fin_len) != fin_len) + return -1; + + return 0; +} + +// given a request, this function creates a struct http_request +static int +http_request_parse(struct http_request *req, char *request) +{ + const char *url; + const char *version; + int url_len, version_len; + + if (!req) + return -1; + + if (strncmp(request, "GET ", 4) != 0) + return -E_BAD_REQ; + + // skip GET + request += 4; + + // get the url + url = request; + while (*request && *request != ' ') + request++; + url_len = request - url; + + req->url = malloc(url_len + 1); + memmove(req->url, url, url_len); + req->url[url_len] = '\0'; + + // skip space + request++; + + version = request; + while (*request && *request != '\n') + request++; + version_len = request - version; + + req->version = malloc(version_len + 1); + memmove(req->version, version, version_len); + req->version[version_len] = '\0'; + + // no entity parsing + + return 0; +} + +static int +send_error(struct http_request *req, int code) +{ + char buf[512]; + int r; + + struct error_messages *e = errors; + while (e->code != 0 && e->msg != 0) { + if (e->code == code) + break; + e++; + } + + if (e->code == 0) + return -1; + + r = snprintf(buf, 512, "HTTP/" HTTP_VERSION" %d %s\r\n" + "Server: jhttpd/" VERSION "\r\n" + "Connection: close" + "Content-type: text/html\r\n" + "\r\n" + "

%d - %s

\r\n", + e->code, e->msg, e->code, e->msg); + + if (write(req->sock, buf, r) != r) + return -1; + + return 0; +} + +static int +send_file(struct http_request *req) +{ + int r; + off_t file_size = -1; + int fd; + struct Stat st; + // open the requested url for reading + // if the file does not exist, send a 404 error using send_error + // if the file is a directory, send a 404 error using send_error + // set file_size to the size of the file + + // LAB 6: Your code here. + // panic("send_file not implemented"); + if ( (r = open(req->url, O_RDONLY) )< 0 ) { + return send_error(req, 404); + + } + fd = r; + // 怎么判断一个fd 是目录, 没有 num2fd + if ( (r = fstat(fd, &st)) < 0) + return send_error(req, 404); + + if (st.st_isdir) + return send_error(req, 404); + + file_size = st.st_size; + + if ((r = send_header(req, 200)) < 0) + goto end; + + if ((r = send_size(req, file_size)) < 0) + goto end; + + if ((r = send_content_type(req)) < 0) + goto end; + + if ((r = send_header_fin(req)) < 0) + goto end; + + r = send_data(req, fd); + +end: + close(fd); + return r; +} + +static void +handle_client(int sock) +{ + struct http_request con_d; + int r; + char buffer[BUFFSIZE]; + int received = -1; + struct http_request *req = &con_d; + + while (1) + { + // Receive message + if ((received = read(sock, buffer, BUFFSIZE)) < 0) + panic("failed to read"); + + memset(req, 0, sizeof(*req)); + + req->sock = sock; + + r = http_request_parse(req, buffer); + if (r == -E_BAD_REQ) + send_error(req, 400); + else if (r < 0) + panic("parse failed"); + else + send_file(req); + + req_free(req); + + // no keep alive + break; + } + + close(sock); +} + +void +umain(int argc, char **argv) +{ + int serversock, clientsock; + struct sockaddr_in server, client; + + binaryname = "jhttpd"; + + // Create the TCP socket + if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + die("Failed to create socket"); + + // Construct the server sockaddr_in structure + memset(&server, 0, sizeof(server)); // Clear struct + server.sin_family = AF_INET; // Internet/IP + server.sin_addr.s_addr = htonl(INADDR_ANY); // IP address + server.sin_port = htons(PORT); // server port + + // Bind the server socket + if (bind(serversock, (struct sockaddr *) &server, + sizeof(server)) < 0) + { + die("Failed to bind the server socket"); + } + + // Listen on the server socket + if (listen(serversock, MAXPENDING) < 0) + die("Failed to listen on server socket"); + + cprintf("Waiting for http connections...\n"); + + while (1) { + unsigned int clientlen = sizeof(client); + // Wait for client connection + if ((clientsock = accept(serversock, + (struct sockaddr *) &client, + &clientlen)) < 0) + { + die("Failed to accept client connection"); + } + handle_client(clientsock); + } + + close(serversock); +} diff --git a/lab/user/sh.c b/lab/user/sh.c index e350998..2fb1f49 100644 --- a/lab/user/sh.c +++ b/lab/user/sh.c @@ -53,6 +53,9 @@ again: // then check whether 'fd' is 0. // If not, dup 'fd' onto file descriptor 0, // then close the original 'fd'. + // LAB 5: Your code here. + // panic("< redirection not implemented"); + if ( (fd = open(t, O_RDONLY) )< 0 ) { fprintf(2,"file %s is no exist\n", t); exit(); @@ -62,8 +65,7 @@ again: close(fd); } - // LAB 5: Your code here. - // panic("< redirection not implemented"); + break; case '>': // Output redirection diff --git a/lab/user/testtime.c b/lab/user/testtime.c new file mode 100644 index 0000000..68e350c --- /dev/null +++ b/lab/user/testtime.c @@ -0,0 +1,35 @@ +#include +#include + +void +sleep(int sec) +{ + unsigned now = sys_time_msec(); + unsigned end = now + sec * 1000; + + if ((int)now < 0 && (int)now > -MAXERROR) + panic("sys_time_msec: %e", (int)now); + if (end < now) + panic("sleep: wrap"); + + while (sys_time_msec() < end) + sys_yield(); +} + +void +umain(int argc, char **argv) +{ + int i; + + // Wait for the console to calm down + for (i = 0; i < 50; i++) + sys_yield(); + + cprintf("starting count down: "); + for (i = 5; i >= 0; i--) { + cprintf("%d ", i); + sleep(1); + } + cprintf("\n"); + breakpoint(); +}